@prenta/core 7.7.0 → 7.7.2

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.
Files changed (106) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/api/route-helpers/auth.d.ts +131 -0
  3. package/dist/api/route-helpers/auth.d.ts.map +1 -0
  4. package/dist/api/route-helpers/auth.js +505 -0
  5. package/dist/api/route-helpers/auth.js.map +1 -0
  6. package/dist/api/route-helpers/authoring.d.ts +32 -0
  7. package/dist/api/route-helpers/authoring.d.ts.map +1 -0
  8. package/dist/api/route-helpers/authoring.js +99 -0
  9. package/dist/api/route-helpers/authoring.js.map +1 -0
  10. package/dist/api/route-helpers/forms.d.ts +46 -0
  11. package/dist/api/route-helpers/forms.d.ts.map +1 -0
  12. package/dist/api/route-helpers/forms.js +435 -0
  13. package/dist/api/route-helpers/forms.js.map +1 -0
  14. package/dist/api/route-helpers/http.d.ts +87 -0
  15. package/dist/api/route-helpers/http.d.ts.map +1 -0
  16. package/dist/api/route-helpers/http.js +286 -0
  17. package/dist/api/route-helpers/http.js.map +1 -0
  18. package/dist/api/route-helpers/media.d.ts +37 -0
  19. package/dist/api/route-helpers/media.d.ts.map +1 -0
  20. package/dist/api/route-helpers/media.js +189 -0
  21. package/dist/api/route-helpers/media.js.map +1 -0
  22. package/dist/api/route-helpers/page-shape.d.ts +31 -0
  23. package/dist/api/route-helpers/page-shape.d.ts.map +1 -0
  24. package/dist/api/route-helpers/page-shape.js +98 -0
  25. package/dist/api/route-helpers/page-shape.js.map +1 -0
  26. package/dist/api/route-helpers.d.ts +8 -358
  27. package/dist/api/route-helpers.d.ts.map +1 -1
  28. package/dist/api/route-helpers.js +7 -1592
  29. package/dist/api/route-helpers.js.map +1 -1
  30. package/dist/api/routes/seo/handlers/analysis.d.ts +4 -0
  31. package/dist/api/routes/seo/handlers/analysis.d.ts.map +1 -0
  32. package/dist/api/routes/seo/handlers/analysis.js +131 -0
  33. package/dist/api/routes/seo/handlers/analysis.js.map +1 -0
  34. package/dist/api/routes/seo/handlers/audits.d.ts +5 -0
  35. package/dist/api/routes/seo/handlers/audits.d.ts.map +1 -0
  36. package/dist/api/routes/seo/handlers/audits.js +121 -0
  37. package/dist/api/routes/seo/handlers/audits.js.map +1 -0
  38. package/dist/api/routes/seo/handlers/content.d.ts +4 -0
  39. package/dist/api/routes/seo/handlers/content.d.ts.map +1 -0
  40. package/dist/api/routes/seo/handlers/content.js +330 -0
  41. package/dist/api/routes/seo/handlers/content.js.map +1 -0
  42. package/dist/api/routes/seo/handlers/issue-helpers.d.ts +37 -0
  43. package/dist/api/routes/seo/handlers/issue-helpers.d.ts.map +1 -0
  44. package/dist/api/routes/seo/handlers/issue-helpers.js +72 -0
  45. package/dist/api/routes/seo/handlers/issue-helpers.js.map +1 -0
  46. package/dist/api/routes/seo/handlers/issues-briefs.d.ts +7 -0
  47. package/dist/api/routes/seo/handlers/issues-briefs.d.ts.map +1 -0
  48. package/dist/api/routes/seo/handlers/issues-briefs.js +341 -0
  49. package/dist/api/routes/seo/handlers/issues-briefs.js.map +1 -0
  50. package/dist/api/routes/seo/handlers/issues-fixes.d.ts +4 -0
  51. package/dist/api/routes/seo/handlers/issues-fixes.d.ts.map +1 -0
  52. package/dist/api/routes/seo/handlers/issues-fixes.js +233 -0
  53. package/dist/api/routes/seo/handlers/issues-fixes.js.map +1 -0
  54. package/dist/api/routes/seo/handlers/issues-list.d.ts +7 -0
  55. package/dist/api/routes/seo/handlers/issues-list.d.ts.map +1 -0
  56. package/dist/api/routes/seo/handlers/issues-list.js +191 -0
  57. package/dist/api/routes/seo/handlers/issues-list.js.map +1 -0
  58. package/dist/api/routes/seo/handlers/links.d.ts +6 -0
  59. package/dist/api/routes/seo/handlers/links.d.ts.map +1 -0
  60. package/dist/api/routes/seo/handlers/links.js +383 -0
  61. package/dist/api/routes/seo/handlers/links.js.map +1 -0
  62. package/dist/api/routes/seo/handlers/overview.d.ts +4 -0
  63. package/dist/api/routes/seo/handlers/overview.d.ts.map +1 -0
  64. package/dist/api/routes/seo/handlers/overview.js +348 -0
  65. package/dist/api/routes/seo/handlers/overview.js.map +1 -0
  66. package/dist/api/routes/seo/handlers/pages.d.ts +2 -0
  67. package/dist/api/routes/seo/handlers/pages.d.ts.map +1 -0
  68. package/dist/api/routes/seo/handlers/pages.js +38 -0
  69. package/dist/api/routes/seo/handlers/pages.js.map +1 -0
  70. package/dist/api/routes/seo/handlers/proposals.d.ts +4 -0
  71. package/dist/api/routes/seo/handlers/proposals.d.ts.map +1 -0
  72. package/dist/api/routes/seo/handlers/proposals.js +93 -0
  73. package/dist/api/routes/seo/handlers/proposals.js.map +1 -0
  74. package/dist/api/routes/seo/handlers/public-files.d.ts +6 -0
  75. package/dist/api/routes/seo/handlers/public-files.d.ts.map +1 -0
  76. package/dist/api/routes/seo/handlers/public-files.js +328 -0
  77. package/dist/api/routes/seo/handlers/public-files.js.map +1 -0
  78. package/dist/api/routes/seo/handlers/shared.d.ts +24 -0
  79. package/dist/api/routes/seo/handlers/shared.d.ts.map +1 -0
  80. package/dist/api/routes/seo/handlers/shared.js +67 -0
  81. package/dist/api/routes/seo/handlers/shared.js.map +1 -0
  82. package/dist/api/routes/seo.d.ts +5 -5
  83. package/dist/api/routes/seo.d.ts.map +1 -1
  84. package/dist/api/routes/seo.js +56 -2610
  85. package/dist/api/routes/seo.js.map +1 -1
  86. package/dist/api/routes/users/handlers/api-keys.d.ts +4 -0
  87. package/dist/api/routes/users/handlers/api-keys.d.ts.map +1 -0
  88. package/dist/api/routes/users/handlers/api-keys.js +164 -0
  89. package/dist/api/routes/users/handlers/api-keys.js.map +1 -0
  90. package/dist/api/routes/users/handlers/invites.d.ts +7 -0
  91. package/dist/api/routes/users/handlers/invites.d.ts.map +1 -0
  92. package/dist/api/routes/users/handlers/invites.js +201 -0
  93. package/dist/api/routes/users/handlers/invites.js.map +1 -0
  94. package/dist/api/routes/users/handlers/users-admin.d.ts +7 -0
  95. package/dist/api/routes/users/handlers/users-admin.d.ts.map +1 -0
  96. package/dist/api/routes/users/handlers/users-admin.js +310 -0
  97. package/dist/api/routes/users/handlers/users-admin.js.map +1 -0
  98. package/dist/api/routes/users/handlers/users-detail.d.ts +16 -0
  99. package/dist/api/routes/users/handlers/users-detail.d.ts.map +1 -0
  100. package/dist/api/routes/users/handlers/users-detail.js +525 -0
  101. package/dist/api/routes/users/handlers/users-detail.js.map +1 -0
  102. package/dist/api/routes/users.d.ts +4 -0
  103. package/dist/api/routes/users.d.ts.map +1 -1
  104. package/dist/api/routes/users.js +38 -1177
  105. package/dist/api/routes/users.js.map +1 -1
  106. package/package.json +1 -1
@@ -0,0 +1,328 @@
1
+ import { getPrentaConfig } from '../../../../config/runtime.js';
2
+ import { recordAiCrawlerFromRequest, recordAiCrawlerHit } from '../../../../seo/ai-crawler-log.js';
3
+ import { detectAiBot } from '../../../../seo/ai-bots.js';
4
+ import { json, errorResponse, internalError, hasModel, requireAuth, } from '../../../route-helpers.js';
5
+ import { db } from './shared.js';
6
+ export async function getLlmsTxt(request) {
7
+ try {
8
+ // Public surface: must exclude soft-deleted docs and internal/system
9
+ // collections (e.g. `__seo_config__`). Over-fetch then filter so the
10
+ // 50-page cap still lands on real content after exclusion.
11
+ const { isInternalCollection } = await import('../../../../seo/audit-runner.js');
12
+ const { siteUrlFromRequest, loadEffectiveConfig } = await import('../../seo-public.js');
13
+ // Layer DB overrides (Settings → SEO) so an admin-edited siteName /
14
+ // defaultMetaDescription flows into the llms heading + summary too, exactly
15
+ // as sitemap.xml, robots.txt, /og.png and the feeds already do.
16
+ const cfg = (await loadEffectiveConfig()) ?? getPrentaConfig();
17
+ const llmsCfg = cfg?.seo?.llms;
18
+ const base = siteUrlFromRequest(request, cfg);
19
+ // Operators can disable the file entirely (it's optionality, not a
20
+ // guaranteed traffic channel).
21
+ if (llmsCfg?.disabled) {
22
+ return new Response('Not found', { status: 404 });
23
+ }
24
+ const maxEntries = llmsCfg?.maxEntries ?? 50;
25
+ const maxPerSection = llmsCfg?.maxPerSection ?? 25;
26
+ // Restrict AUTO selection to the configured high-value collections when
27
+ // set, so blog archives don't flood the curated guide.
28
+ const eligible = Array.isArray(llmsCfg?.eligibleCollections) && llmsCfg.eligibleCollections.length > 0
29
+ ? new Set(llmsCfg.eligibleCollections)
30
+ : null;
31
+ const rawDocs = await db().document.findMany({
32
+ where: { status: 'PUBLISHED', deletedAt: null },
33
+ select: { title: true, slug: true, collection: true, updatedAt: true, data: true },
34
+ orderBy: { updatedAt: 'desc' },
35
+ take: 500,
36
+ });
37
+ // Per-doc curation + privacy boundary (noindex exclusion) live in the
38
+ // shared selector so `/llms.txt` and `/llms-full.txt` can never diverge.
39
+ const { selectLlmsDocs, toLlmsPage } = await import('../../../../seo/llms-select.js');
40
+ const selected = selectLlmsDocs(rawDocs, { isInternalCollection, eligible });
41
+ const { generateLlmsTxt } = await import('../../../../seo/llms-txt.js');
42
+ const pages = selected.map((d) => toLlmsPage(d, {
43
+ base,
44
+ urlPrefix: cfg?.collections?.[d.collection]?.urlPrefix,
45
+ trailingSlash: cfg?.seo?.trailingSlash,
46
+ }));
47
+ const txt = generateLlmsTxt({
48
+ siteName: cfg?.seo?.siteName || 'Prenta CMS',
49
+ siteUrl: base,
50
+ siteDescription: llmsCfg?.summary || cfg?.seo?.defaultMetaDescription || 'Content managed by Prenta CMS',
51
+ intro: llmsCfg?.intro,
52
+ maxPages: maxEntries,
53
+ maxPerSection,
54
+ }, pages);
55
+ // Opt-in honeypot: append a unique trap link. No human reads llms.txt, so
56
+ // a fetch of this URL is an automated reader — recorded under the
57
+ // "honeypot" surface in the AI-crawler panel.
58
+ let body = txt;
59
+ if (llmsCfg?.honeypot) {
60
+ const trapUrl = `${base.replace(/\/$/, '')}/api/cms/ai-crawlers/trap`;
61
+ body += `\n## Reference\n\n- [Complete content index](${trapUrl}): Full machine-readable content map.\n`;
62
+ }
63
+ // Measurement: if a known AI crawler fetched the index, count it.
64
+ await recordAiCrawlerFromRequest(db(), request, 'llms');
65
+ return new Response(body, {
66
+ status: 200,
67
+ headers: {
68
+ 'Content-Type': 'text/plain; charset=utf-8',
69
+ // The file itself is a machine-facing index, not a page to rank.
70
+ 'X-Robots-Tag': 'noindex',
71
+ // Same CDN TTL as robots.txt — the index is cheap to regenerate
72
+ // after publish but should not be fetched on every agent request.
73
+ 'Cache-Control': 'public, max-age=3600, s-maxage=3600',
74
+ },
75
+ });
76
+ }
77
+ catch (err) {
78
+ return internalError(err, 'llms.txt');
79
+ }
80
+ }
81
+ // `/llms-full.txt` — same curated selection as `/llms.txt`, but inlines each
82
+ // page's content rendered to clean Markdown so an agent can read the actual
83
+ // body without fetching every URL. Opt-in (these files get large) and bounded
84
+ // by a byte budget. noindex + never in the sitemap; HTML stays canonical.
85
+ export async function getLlmsFullTxt(request) {
86
+ try {
87
+ const { isInternalCollection } = await import('../../../../seo/audit-runner.js');
88
+ const { siteUrlFromRequest, loadEffectiveConfig } = await import('../../seo-public.js');
89
+ // Honor admin overrides (siteName / summary / defaultMetaDescription) the
90
+ // same way `/llms.txt` and the other public SEO surfaces do.
91
+ const cfg = (await loadEffectiveConfig()) ?? getPrentaConfig();
92
+ const llmsCfg = cfg?.seo?.llms;
93
+ const base = siteUrlFromRequest(request, cfg);
94
+ // Gated independently from `/llms.txt`: must be both enabled overall and
95
+ // have the full variant turned on.
96
+ if (llmsCfg?.disabled || !llmsCfg?.fullEnabled) {
97
+ return new Response('Not found', { status: 404 });
98
+ }
99
+ const maxEntries = llmsCfg?.maxEntries ?? 50;
100
+ const maxBytes = llmsCfg?.maxFullBytes ?? 2_000_000;
101
+ const eligible = Array.isArray(llmsCfg?.eligibleCollections) && llmsCfg.eligibleCollections.length > 0
102
+ ? new Set(llmsCfg.eligibleCollections)
103
+ : null;
104
+ const rawDocs = await db().document.findMany({
105
+ where: { status: 'PUBLISHED', deletedAt: null },
106
+ select: {
107
+ title: true,
108
+ slug: true,
109
+ collection: true,
110
+ updatedAt: true,
111
+ data: true,
112
+ plainText: true,
113
+ },
114
+ orderBy: { updatedAt: 'desc' },
115
+ take: 500,
116
+ });
117
+ const { selectLlmsDocs, toLlmsPage } = await import('../../../../seo/llms-select.js');
118
+ const { documentToMarkdown } = await import('../../../../seo/content-markdown.js');
119
+ const selected = selectLlmsDocs(rawDocs, { isInternalCollection, eligible }).slice(0, maxEntries);
120
+ const header = [
121
+ `# ${cfg?.seo?.siteName || 'Prenta CMS'}`,
122
+ '',
123
+ `> ${llmsCfg?.summary || cfg?.seo?.defaultMetaDescription || 'Full content export for AI agents.'}`,
124
+ '',
125
+ ].join('\n');
126
+ const chunks = [header];
127
+ let bytes = Buffer.byteLength(header, 'utf8');
128
+ let truncated = false;
129
+ for (const d of selected) {
130
+ const page = toLlmsPage(d, {
131
+ base,
132
+ urlPrefix: cfg?.collections?.[d.collection]?.urlPrefix,
133
+ trailingSlash: cfg?.seo?.trailingSlash,
134
+ });
135
+ const md = documentToMarkdown({
136
+ title: d.title,
137
+ data: d.data,
138
+ plainText: d.plainText,
139
+ }, { baseUrl: base });
140
+ // Each entry is delimited by a horizontal rule and carries its canonical
141
+ // URL so the inlined content stays traceable back to the live page.
142
+ const block = `\n---\n\nURL: ${page.url}\n\n${md}\n`;
143
+ const blockBytes = Buffer.byteLength(block, 'utf8');
144
+ if (bytes + blockBytes > maxBytes) {
145
+ truncated = true;
146
+ break;
147
+ }
148
+ chunks.push(block);
149
+ bytes += blockBytes;
150
+ }
151
+ if (truncated) {
152
+ chunks.push('\n---\n\n> Output truncated at the configured size limit.\n');
153
+ }
154
+ await recordAiCrawlerFromRequest(db(), request, 'llms-full');
155
+ return new Response(chunks.join(''), {
156
+ status: 200,
157
+ headers: {
158
+ 'Content-Type': 'text/plain; charset=utf-8',
159
+ 'X-Robots-Tag': 'noindex',
160
+ 'Cache-Control': 'public, max-age=3600, s-maxage=3600',
161
+ },
162
+ });
163
+ }
164
+ catch (err) {
165
+ return internalError(err, 'llms-full.txt');
166
+ }
167
+ }
168
+ // Per-page Markdown: `GET /md?path=/about` returns a clean Markdown rendering
169
+ // of a single published page so an agent can fetch the content, not just the
170
+ // link. Fresh installs expose this at clean `/{path}.md` URLs via a scaffolded
171
+ // rewrite. CRITICAL: always noindex, never in the sitemap — the HTML page
172
+ // stays canonical (per-page `.md` is the most common duplicate-content trap).
173
+ export async function getMarkdownForPath(request) {
174
+ try {
175
+ const cfg = getPrentaConfig();
176
+ const url = new URL(request.url);
177
+ const pathParam = url.searchParams.get('path');
178
+ if (!pathParam) {
179
+ return errorResponse('Missing required "path" query parameter', 400);
180
+ }
181
+ // Part of the AI/agent layer — honor the same master switch as llms.txt.
182
+ if (cfg?.seo?.llms?.disabled) {
183
+ return new Response('Not found', { status: 404 });
184
+ }
185
+ const { matchPathToCollection } = await import('../../../../seo/resolve-path.js');
186
+ const match = matchPathToCollection(cfg, pathParam);
187
+ if (!match) {
188
+ return new Response('Not found', { status: 404 });
189
+ }
190
+ const slugFilter = match.isRoot
191
+ ? {
192
+ OR: [
193
+ { data: { path: ['slug'], equals: 'home' } },
194
+ { data: { path: ['slug'], equals: 'index' } },
195
+ { slug: 'home' },
196
+ { slug: 'index' },
197
+ ],
198
+ }
199
+ : {
200
+ OR: [{ data: { path: ['slug'], equals: match.slug } }, { slug: match.slug }],
201
+ };
202
+ const doc = await db().document.findFirst({
203
+ where: {
204
+ collection: match.collection,
205
+ deletedAt: null,
206
+ status: 'PUBLISHED',
207
+ ...slugFilter,
208
+ },
209
+ select: { title: true, data: true, plainText: true },
210
+ });
211
+ if (!doc) {
212
+ return new Response('Not found', { status: 404 });
213
+ }
214
+ const data = doc.data || {};
215
+ // The selection filter is the privacy boundary: noindexed or explicitly
216
+ // excluded pages must not be fetchable as Markdown either.
217
+ const { isLlmsNoindex, readLlmsInclude } = await import('../../../../seo/llms-select.js');
218
+ if (isLlmsNoindex(data) || readLlmsInclude(data) === 'NEVER') {
219
+ return new Response('Not found', { status: 404 });
220
+ }
221
+ const { documentToMarkdown } = await import('../../../../seo/content-markdown.js');
222
+ const { siteUrlFromRequest } = await import('../../seo-public.js');
223
+ const base = siteUrlFromRequest(request, cfg);
224
+ const md = documentToMarkdown({ title: doc.title, data, plainText: doc.plainText }, { baseUrl: base });
225
+ await recordAiCrawlerFromRequest(db(), request, 'md');
226
+ return new Response(md, {
227
+ status: 200,
228
+ headers: {
229
+ 'Content-Type': 'text/markdown; charset=utf-8',
230
+ 'X-Robots-Tag': 'noindex',
231
+ },
232
+ });
233
+ }
234
+ catch (err) {
235
+ return internalError(err, 'md');
236
+ }
237
+ }
238
+ // Honeypot: a unique trap URL appended to llms.txt only when
239
+ // `seo.llms.honeypot` is enabled. No human visits it, so any fetch is an
240
+ // automated reader that followed a link from the file. Always records (even
241
+ // if the flag was later turned off, a cached llms.txt may still point here)
242
+ // and returns a tiny noindex body.
243
+ export async function getAiCrawlerTrap(request) {
244
+ try {
245
+ const detected = detectAiBot(request.headers.get('user-agent'));
246
+ await recordAiCrawlerHit(db(), {
247
+ bot: detected?.token ?? 'unknown',
248
+ surface: 'honeypot',
249
+ category: detected?.category ?? 'other',
250
+ });
251
+ return new Response('This endpoint exists to detect automated indexers.\n', {
252
+ status: 200,
253
+ headers: {
254
+ 'Content-Type': 'text/plain; charset=utf-8',
255
+ 'X-Robots-Tag': 'noindex',
256
+ },
257
+ });
258
+ }
259
+ catch (err) {
260
+ return internalError(err, 'ai-crawlers/trap');
261
+ }
262
+ }
263
+ // Admin: aggregated AI-crawler activity for the SEO → Technical panel.
264
+ // Any authenticated admin may read it (analytics, not a mutation).
265
+ export async function getAiCrawlers(request) {
266
+ try {
267
+ const auth = await requireAuth(request);
268
+ if (auth.error)
269
+ return auth.error;
270
+ if (!hasModel(db(), 'aiCrawlerHit')) {
271
+ return json({
272
+ data: { enabled: false, rows: [], totals: { hits: 0, bots: 0, honeypot: 0 } },
273
+ });
274
+ }
275
+ const rows = await db().aiCrawlerHit.findMany({
276
+ orderBy: { lastSeenAt: 'desc' },
277
+ take: 500,
278
+ });
279
+ const totalHits = rows.reduce((sum, r) => sum + Number(r.hits ?? 0), 0);
280
+ const honeypotHits = rows
281
+ .filter((r) => r.surface === 'honeypot')
282
+ .reduce((sum, r) => sum + Number(r.hits ?? 0), 0);
283
+ const uniqueBots = new Set(rows.map((r) => r.bot)).size;
284
+ // Recency windowing. `hits` is a lifetime cumulative counter (one row per
285
+ // bot×surface), so we can't derive per-window hit *counts* from it — but
286
+ // `lastSeenAt` lets us answer the actionable question "which bots/surfaces
287
+ // were active in the last 24h / 7d". Per-window hit-count trends would
288
+ // need a (bot, surface, day) bucket table (a future additive migration);
289
+ // this keeps storage bounded while making recency visible today.
290
+ const nowMs = Date.now();
291
+ const DAY_MS = 24 * 60 * 60 * 1000;
292
+ const within = (v, ms) => {
293
+ if (!v)
294
+ return false;
295
+ const t = v instanceof Date ? v.getTime() : new Date(v).getTime();
296
+ return Number.isFinite(t) && nowMs - t <= ms;
297
+ };
298
+ const windowSummary = (ms) => {
299
+ const active = rows.filter((r) => within(r.lastSeenAt, ms));
300
+ return {
301
+ bots: new Set(active.map((r) => r.bot)).size,
302
+ surfaces: new Set(active.map((r) => r.surface)).size,
303
+ rows: active.length,
304
+ };
305
+ };
306
+ return json({
307
+ data: {
308
+ enabled: true,
309
+ rows: rows.map((r) => ({
310
+ bot: r.bot,
311
+ surface: r.surface,
312
+ category: r.category,
313
+ hits: Number(r.hits ?? 0),
314
+ firstSeenAt: r.firstSeenAt,
315
+ lastSeenAt: r.lastSeenAt,
316
+ activeLast24h: within(r.lastSeenAt, DAY_MS),
317
+ activeLast7d: within(r.lastSeenAt, 7 * DAY_MS),
318
+ })),
319
+ totals: { hits: totalHits, bots: uniqueBots, honeypot: honeypotHits },
320
+ windows: { last24h: windowSummary(DAY_MS), last7d: windowSummary(7 * DAY_MS) },
321
+ },
322
+ });
323
+ }
324
+ catch (err) {
325
+ return internalError(err, 'ai-crawlers');
326
+ }
327
+ }
328
+ //# sourceMappingURL=public-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-files.js","sourceRoot":"","sources":["../../../../../src/api/routes/seo/handlers/public-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { assertPlanFeature } from '../../../../plan/features.js';
2
+ import { guardedDb } from '../../../guarded-db.js';
3
+ import { type AuthSession } from '../../../route-helpers.js';
4
+ export declare const db: typeof guardedDb;
5
+ export declare const seoLogger: import("../../../../diagnostics/logger.js").ScopedLogger;
6
+ export declare function planRequiredResponse(denial: NonNullable<ReturnType<typeof assertPlanFeature>>): Response;
7
+ export declare function mapInternalLinkInsertError(err: unknown): Response | null;
8
+ export declare function seoAiGenerationError(result: {
9
+ code: string;
10
+ message: string;
11
+ }): Response;
12
+ /** Sort issues by risk (critical → warning → info), then most-recent first. */
13
+ export declare function sortIssuesBySeverity<T extends {
14
+ severity?: unknown;
15
+ lastDetectedAt?: unknown;
16
+ }>(issues: T[]): T[];
17
+ /**
18
+ * Content-writing fix strategies (`add-section`, `insert-link`, `fix-link`)
19
+ * write a document the route cannot name up front, so the API-key collection
20
+ * scope is checked inside `applyIssueFix` once the collection is known. JWT
21
+ * sessions already passed the route's `WRITE_ROLES` check and are never denied.
22
+ */
23
+ export declare function contentWriteGate(session: AuthSession): (collection: string) => string | null;
24
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../../src/api/routes/seo/handlers/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,2BAA2B,CAAA;AAElC,eAAO,MAAM,EAAE,kBAAY,CAAA;AAK3B,eAAO,MAAM,SAAS,0DAAsB,CAAA;AAO5C,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,WAAW,CAAC,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,GACxD,QAAQ,CAUV;AAED,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAgBxE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAQxF;AACD,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,EAC7F,MAAM,EAAE,CAAC,EAAE,GACV,CAAC,EAAE,CAQL;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAE5F"}
@@ -0,0 +1,67 @@
1
+ import { createLogger } from '../../../../diagnostics/logger.js';
2
+ import { guardedDb } from '../../../guarded-db.js';
3
+ import { apiKeyCollectionScopeDenial, errorResponse, json, } from '../../../route-helpers.js';
4
+ export const db = guardedDb;
5
+ // Explicit risk order. A plain `severity: 'asc'` DB sort orders the values
6
+ // alphabetically (critical, info, warning) which wrongly ranks `info` above
7
+ // `warning`. Unknown severities sort last.
8
+ export const seoLogger = createLogger('seo');
9
+ const SEVERITY_RANK = { critical: 0, warning: 1, info: 2 };
10
+ function severityRank(severity) {
11
+ return SEVERITY_RANK[String(severity)] ?? 99;
12
+ }
13
+ export function planRequiredResponse(denial) {
14
+ return json({
15
+ error: denial.message,
16
+ code: 'plan_required',
17
+ requiredTier: denial.requiredTier,
18
+ feature: denial.feature,
19
+ }, 403);
20
+ }
21
+ export function mapInternalLinkInsertError(err) {
22
+ const msg = err instanceof Error ? err.message : '';
23
+ switch (msg) {
24
+ case 'SOURCE_NOT_FOUND':
25
+ return errorResponse('Source document not found', 404);
26
+ case 'TARGET_NOT_FOUND':
27
+ return errorResponse('Target document not found', 404);
28
+ case 'NOT_WRAPPABLE':
29
+ return errorResponse('Anchor is not wrappable in the live source document.', 422);
30
+ case 'ALREADY_LINKED':
31
+ return errorResponse('Source already links to this target.', 422);
32
+ case 'TARGET_PATH_MISMATCH':
33
+ return errorResponse('Target path does not match the live document.', 422);
34
+ default:
35
+ return null;
36
+ }
37
+ }
38
+ export function seoAiGenerationError(result) {
39
+ if (result.code === 'unavailable' || result.code === 'feature_disabled') {
40
+ return errorResponse(result.message, 503);
41
+ }
42
+ if (result.code === 'rate_limited' || result.code === 'budget_exceeded') {
43
+ return errorResponse(result.message, 429);
44
+ }
45
+ return errorResponse(result.message, 422);
46
+ }
47
+ /** Sort issues by risk (critical → warning → info), then most-recent first. */
48
+ export function sortIssuesBySeverity(issues) {
49
+ return [...issues].sort((a, b) => {
50
+ const rankDiff = severityRank(a.severity) - severityRank(b.severity);
51
+ if (rankDiff !== 0)
52
+ return rankDiff;
53
+ const at = a.lastDetectedAt ? new Date(a.lastDetectedAt).getTime() : 0;
54
+ const bt = b.lastDetectedAt ? new Date(b.lastDetectedAt).getTime() : 0;
55
+ return bt - at;
56
+ });
57
+ }
58
+ /**
59
+ * Content-writing fix strategies (`add-section`, `insert-link`, `fix-link`)
60
+ * write a document the route cannot name up front, so the API-key collection
61
+ * scope is checked inside `applyIssueFix` once the collection is known. JWT
62
+ * sessions already passed the route's `WRITE_ROLES` check and are never denied.
63
+ */
64
+ export function contentWriteGate(session) {
65
+ return (collection) => apiKeyCollectionScopeDenial(session, collection, 'update');
66
+ }
67
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../../../src/api/routes/seo/handlers/shared.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import type { ApiRouter } from '../router.js';
2
- /** Sort issues by risk (critical → warning → info), then most-recent first. */
3
- export declare function sortIssuesBySeverity<T extends {
4
- severity?: unknown;
5
- lastDetectedAt?: unknown;
6
- }>(issues: T[]): T[];
2
+ export { sortIssuesBySeverity } from './seo/handlers/shared.js';
3
+ /**
4
+ * SEO HTTP routes. Handlers live under `./seo/handlers` so this file stays a
5
+ * registration list. Request behavior is unchanged.
6
+ */
7
7
  export declare function registerSeoRoutes(router: ApiRouter): void;
8
8
  //# sourceMappingURL=seo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../../../src/api/routes/seo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AA4G7C,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,EAC7F,MAAM,EAAE,CAAC,EAAE,GACV,CAAC,EAAE,CAQL;AAYD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0vFzD"}
1
+ {"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../../../src/api/routes/seo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAmD7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAE/D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA2CzD"}