@rankmyseo/server 0.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/index.cjs ADDED
@@ -0,0 +1,571 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createHandler: () => createHandler,
24
+ readScope: () => readScope
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/handler.ts
29
+ var import_server_only2 = require("server-only");
30
+ var import_core2 = require("@rankmyseo/core");
31
+
32
+ // src/routes.ts
33
+ var import_server_only = require("server-only");
34
+ var import_node_crypto = require("crypto");
35
+ var import_core = require("@rankmyseo/core");
36
+ var import_agent = require("@rankmyseo/agent");
37
+
38
+ // src/utils.ts
39
+ function readScope(request) {
40
+ const tenantId = request.headers.get("x-tenant-id");
41
+ const projectId = request.headers.get("x-project-id");
42
+ if (!tenantId || !projectId) {
43
+ return Response.json(
44
+ { error: "Missing or invalid x-tenant-id / x-project-id headers" },
45
+ { status: 400 }
46
+ );
47
+ }
48
+ return { tenantId, projectId };
49
+ }
50
+ async function readJson(request) {
51
+ try {
52
+ return await request.json();
53
+ } catch {
54
+ return Response.json({ error: "Invalid JSON body" }, { status: 400 });
55
+ }
56
+ }
57
+ function acceptsMarkdown(request) {
58
+ const accept = request.headers.get("accept") ?? "";
59
+ return accept.includes("text/markdown");
60
+ }
61
+ function buildSitemapXml(routes2, baseUrl) {
62
+ const urls = routes2.map(
63
+ (route) => ` <url><loc>${baseUrl}${route === "/" ? "" : route}</loc></url>`
64
+ ).join("\n");
65
+ return `<?xml version="1.0" encoding="UTF-8"?>
66
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
67
+ ${urls}
68
+ </urlset>`;
69
+ }
70
+ function buildLlmsTxt(config) {
71
+ const name = config.llmsTxt?.projectName ?? "RankMySEO Project";
72
+ const summary = config.llmsTxt?.summary ?? "SEO tracking, audits, and rank history for this site.";
73
+ const links = config.llmsTxt?.links ?? [
74
+ { title: "Documentation", url: "/docs.md" }
75
+ ];
76
+ const linkBlock = links.map((l) => `- [${l.title}](${l.url})`).join("\n");
77
+ return `# ${name}
78
+
79
+ > ${summary}
80
+
81
+ ## Resources
82
+
83
+ ${linkBlock}
84
+ `;
85
+ }
86
+ function pageToMarkdown(pathname, title) {
87
+ return `# ${title}
88
+
89
+ Path: \`${pathname}\`
90
+
91
+ This page is available as Markdown for AI agents.
92
+ `;
93
+ }
94
+ function withMarkdownNegotiation(request, html, markdown, pathname) {
95
+ const wantsMarkdown = acceptsMarkdown(request);
96
+ const accept = request.headers.get("accept") ?? "";
97
+ if (wantsMarkdown) {
98
+ return new Response(markdown, {
99
+ status: 200,
100
+ headers: {
101
+ "Content-Type": "text/markdown; charset=utf-8",
102
+ Vary: "Accept",
103
+ Link: `<${pathname}?format=html>; rel="alternate"; type="text/html"`
104
+ }
105
+ });
106
+ }
107
+ if (accept && !accept.includes("*/*") && !accept.includes("text/html")) {
108
+ return Response.json(
109
+ { error: "Not acceptable", supported: ["text/html", "text/markdown"] },
110
+ { status: 406, headers: { Vary: "Accept" } }
111
+ );
112
+ }
113
+ return new Response(html, {
114
+ status: 200,
115
+ headers: {
116
+ "Content-Type": "text/html; charset=utf-8",
117
+ Vary: "Accept",
118
+ Link: `<${pathname}>; rel="alternate"; type="text/markdown"`
119
+ }
120
+ });
121
+ }
122
+
123
+ // src/routes.ts
124
+ var routes = [];
125
+ function addRoute(method, pattern, handler) {
126
+ routes.push({ method, pattern, handler });
127
+ }
128
+ addRoute("GET", /^\/projects$/, async (_req, ctx) => {
129
+ const data = await ctx.store.projects.list(ctx.scope);
130
+ return Response.json({ data });
131
+ });
132
+ addRoute("POST", /^\/projects$/, async (request, ctx) => {
133
+ const body = await readJson(request);
134
+ if (body instanceof Response) return body;
135
+ const parsed = import_core.projectSchema.omit({ createdAt: true, updatedAt: true }).safeParse(body);
136
+ if (!parsed.success) {
137
+ return Response.json({ error: "Invalid project", details: parsed.error.flatten() }, { status: 400 });
138
+ }
139
+ const project = await ctx.store.projects.create({
140
+ ...parsed.data,
141
+ tenantId: ctx.scope.tenantId
142
+ });
143
+ return Response.json({ data: project }, { status: 201 });
144
+ });
145
+ addRoute("GET", /^\/projects\/([^/]+)$/, async (_req, ctx, params) => {
146
+ const project = await ctx.store.projects.getById(ctx.scope, params[1]);
147
+ if (!project) return Response.json({ error: "Not found" }, { status: 404 });
148
+ return Response.json({ data: project });
149
+ });
150
+ addRoute("GET", /^\/keywords$/, async (_req, ctx) => {
151
+ const data = await ctx.store.keywords.list(ctx.scope);
152
+ return Response.json({ data });
153
+ });
154
+ addRoute("POST", /^\/keywords$/, async (request, ctx) => {
155
+ const body = await readJson(request);
156
+ if (body instanceof Response) return body;
157
+ const parsed = import_core.createKeywordInputSchema.safeParse({
158
+ ...body,
159
+ tenantId: ctx.scope.tenantId,
160
+ projectId: ctx.scope.projectId
161
+ });
162
+ if (!parsed.success) {
163
+ return Response.json({ error: "Invalid keyword", details: parsed.error.flatten() }, { status: 400 });
164
+ }
165
+ const keyword = await ctx.store.keywords.create(parsed.data);
166
+ return Response.json({ data: keyword }, { status: 201 });
167
+ });
168
+ addRoute("GET", /^\/keywords\/([^/]+)$/, async (_req, ctx, params) => {
169
+ const keyword = await ctx.store.keywords.getById(ctx.scope, params[1]);
170
+ if (!keyword) return Response.json({ error: "Not found" }, { status: 404 });
171
+ return Response.json({ data: keyword });
172
+ });
173
+ addRoute("DELETE", /^\/keywords\/([^/]+)$/, async (_req, ctx, params) => {
174
+ const deleted = await ctx.store.keywords.delete(ctx.scope, params[1]);
175
+ if (!deleted) return Response.json({ error: "Not found" }, { status: 404 });
176
+ return new Response(null, { status: 204 });
177
+ });
178
+ addRoute("POST", /^\/snapshots$/, async (request, ctx) => {
179
+ const body = await readJson(request);
180
+ if (body instanceof Response) return body;
181
+ const parsed = import_core.createRankSnapshotInputSchema.safeParse({
182
+ ...body,
183
+ tenantId: ctx.scope.tenantId,
184
+ projectId: ctx.scope.projectId
185
+ });
186
+ if (!parsed.success) {
187
+ return Response.json({ error: "Invalid snapshot", details: parsed.error.flatten() }, { status: 400 });
188
+ }
189
+ const snapshot = await ctx.store.snapshots.append(parsed.data);
190
+ return Response.json({ data: snapshot }, { status: 201 });
191
+ });
192
+ addRoute("GET", /^\/snapshots$/, async (_req, ctx, _params, url) => {
193
+ const parsed = import_core.snapshotRangeQuerySchema.safeParse({
194
+ tenantId: ctx.scope.tenantId,
195
+ projectId: ctx.scope.projectId,
196
+ keywordId: url.searchParams.get("keywordId") ?? void 0,
197
+ from: url.searchParams.get("from"),
198
+ to: url.searchParams.get("to")
199
+ });
200
+ if (!parsed.success) {
201
+ return Response.json({ error: "Invalid query", details: parsed.error.flatten() }, { status: 400 });
202
+ }
203
+ const data = await ctx.store.snapshots.listByRange(parsed.data);
204
+ return Response.json({ data });
205
+ });
206
+ addRoute("GET", /^\/audits$/, async (_req, ctx) => {
207
+ const data = await ctx.store.audits.list(ctx.scope);
208
+ return Response.json({ data });
209
+ });
210
+ addRoute("POST", /^\/audits$/, async (request, ctx) => {
211
+ const body = await readJson(request);
212
+ if (body instanceof Response) return body;
213
+ const parsed = import_core.createAuditInputSchema.safeParse({
214
+ ...body,
215
+ tenantId: ctx.scope.tenantId,
216
+ projectId: ctx.scope.projectId
217
+ });
218
+ if (!parsed.success) {
219
+ return Response.json({ error: "Invalid audit", details: parsed.error.flatten() }, { status: 400 });
220
+ }
221
+ const audit = await ctx.store.audits.create({
222
+ ...parsed.data,
223
+ id: (0, import_node_crypto.randomUUID)()
224
+ });
225
+ return Response.json({ data: audit }, { status: 201 });
226
+ });
227
+ addRoute("GET", /^\/audits\/([^/]+)$/, async (_req, ctx, params) => {
228
+ const audit = await ctx.store.audits.getById(ctx.scope, params[1]);
229
+ if (!audit) return Response.json({ error: "Not found" }, { status: 404 });
230
+ return Response.json({ data: audit });
231
+ });
232
+ addRoute("POST", /^\/collect$/, async (request, ctx) => {
233
+ if (!ctx.config.siteFeatures.collector) {
234
+ return Response.json({ error: "Collector disabled" }, { status: 403 });
235
+ }
236
+ const body = await readJson(request);
237
+ if (body instanceof Response) return body;
238
+ const parsed = import_core.pageSignalsSchema.safeParse(body);
239
+ if (!parsed.success) {
240
+ return Response.json({ error: "Invalid signals", details: parsed.error.flatten() }, { status: 400 });
241
+ }
242
+ const { checks, score } = (0, import_core.runAuditChecks)(parsed.data);
243
+ const audit = await ctx.store.audits.create({
244
+ id: (0, import_node_crypto.randomUUID)(),
245
+ tenantId: ctx.scope.tenantId,
246
+ projectId: ctx.scope.projectId,
247
+ url: parsed.data.url,
248
+ score,
249
+ checks
250
+ });
251
+ return Response.json({ data: audit }, { status: 201 });
252
+ });
253
+ addRoute("POST", /^\/scan$/, async (request, ctx) => {
254
+ const body = await readJson(request);
255
+ if (body instanceof Response) return body;
256
+ let target;
257
+ try {
258
+ target = (0, import_core.normalizeHttpUrl)(String(body.url ?? ""));
259
+ } catch {
260
+ return Response.json({ error: "A valid url is required" }, { status: 400 });
261
+ }
262
+ if (target.protocol !== "http:" && target.protocol !== "https:") {
263
+ return Response.json({ error: "Only http(s) URLs can be scanned" }, { status: 400 });
264
+ }
265
+ let html;
266
+ try {
267
+ const res = await fetch(target.toString(), {
268
+ headers: { "user-agent": "RankMySEO-Scanner/1.0" },
269
+ redirect: "follow"
270
+ });
271
+ if (!res.ok) {
272
+ return Response.json(
273
+ { error: `Fetch failed with status ${res.status}` },
274
+ { status: 502 }
275
+ );
276
+ }
277
+ html = await res.text();
278
+ } catch {
279
+ return Response.json({ error: "Could not fetch the target URL" }, { status: 502 });
280
+ }
281
+ const signals = (0, import_core.extractPageSignals)(html, target.toString());
282
+ const { checks, score } = (0, import_core.runAuditChecks)(signals);
283
+ const audit = await ctx.store.audits.create({
284
+ id: (0, import_node_crypto.randomUUID)(),
285
+ tenantId: ctx.scope.tenantId,
286
+ projectId: ctx.scope.projectId,
287
+ url: target.toString(),
288
+ score,
289
+ checks
290
+ });
291
+ const recommendations = (0, import_core.buildAuditRecommendations)(checks);
292
+ return Response.json(
293
+ { data: { audit, signals, recommendations } },
294
+ { status: 201 }
295
+ );
296
+ });
297
+ addRoute("POST", /^\/meta\/generate$/, async (request, _ctx) => {
298
+ const body = await readJson(request);
299
+ if (body instanceof Response) return body;
300
+ if (!body.title || !body.title.trim()) {
301
+ return Response.json({ error: "title is required" }, { status: 400 });
302
+ }
303
+ const meta = (0, import_core.generateMeta)({
304
+ title: body.title,
305
+ content: body.content,
306
+ targetKeyword: body.targetKeyword,
307
+ url: body.url,
308
+ siteName: body.siteName
309
+ });
310
+ const { checks, score } = (0, import_core.runAuditChecks)({
311
+ url: body.url && /^https?:\/\//.test(body.url) ? body.url : "https://example.com",
312
+ title: meta.metaTitle,
313
+ metaDescription: meta.metaDescription,
314
+ canonical: meta.canonical && /^https?:\/\//.test(meta.canonical) ? meta.canonical : null,
315
+ h1Count: 1,
316
+ hasOgTags: true,
317
+ hasJsonLd: true
318
+ });
319
+ return Response.json({ data: { meta, checks, score } });
320
+ });
321
+ function blogDisabled(ctx) {
322
+ if (!ctx.config.siteFeatures.blog) {
323
+ return Response.json({ error: "Blog module disabled" }, { status: 403 });
324
+ }
325
+ return null;
326
+ }
327
+ addRoute("GET", /^\/blog$/, async (_req, ctx) => {
328
+ const denied = blogDisabled(ctx);
329
+ if (denied) return denied;
330
+ const data = await ctx.store.blog.list(ctx.scope);
331
+ return Response.json({ data });
332
+ });
333
+ addRoute("POST", /^\/blog$/, async (request, ctx) => {
334
+ const denied = blogDisabled(ctx);
335
+ if (denied) return denied;
336
+ const body = await readJson(request);
337
+ if (body instanceof Response) return body;
338
+ const parsed = import_core.createBlogPostInputSchema.safeParse({
339
+ ...body,
340
+ tenantId: ctx.scope.tenantId,
341
+ projectId: ctx.scope.projectId
342
+ });
343
+ if (!parsed.success) {
344
+ return Response.json({ error: "Invalid blog post", details: parsed.error.flatten() }, { status: 400 });
345
+ }
346
+ const input = parsed.data;
347
+ const slug = input.slug?.trim() || (0, import_core.slugify)(input.title);
348
+ let { metaTitle, metaDescription } = input;
349
+ if (!metaTitle.trim() || !metaDescription.trim()) {
350
+ const meta = (0, import_core.generateMeta)({
351
+ title: input.title,
352
+ content: input.content,
353
+ targetKeyword: input.targetKeyword
354
+ });
355
+ metaTitle = metaTitle.trim() || meta.metaTitle;
356
+ metaDescription = metaDescription.trim() || meta.metaDescription;
357
+ }
358
+ const post = await ctx.store.blog.create({
359
+ id: (0, import_node_crypto.randomUUID)(),
360
+ tenantId: ctx.scope.tenantId,
361
+ projectId: ctx.scope.projectId,
362
+ title: input.title,
363
+ slug,
364
+ content: input.content,
365
+ targetKeyword: input.targetKeyword,
366
+ intent: input.intent,
367
+ metaTitle,
368
+ metaDescription,
369
+ status: input.status
370
+ });
371
+ return Response.json({ data: post }, { status: 201 });
372
+ });
373
+ addRoute("GET", /^\/blog\/([^/]+)$/, async (_req, ctx, params) => {
374
+ const denied = blogDisabled(ctx);
375
+ if (denied) return denied;
376
+ const post = await ctx.store.blog.getById(ctx.scope, params[1]);
377
+ if (!post) return Response.json({ error: "Not found" }, { status: 404 });
378
+ const recommendations = (0, import_core.buildBlogRecommendations)({
379
+ intent: post.intent,
380
+ targetKeyword: post.targetKeyword,
381
+ metaTitle: post.metaTitle,
382
+ metaDescription: post.metaDescription,
383
+ content: post.content
384
+ });
385
+ return Response.json({ data: post, recommendations });
386
+ });
387
+ addRoute("PUT", /^\/blog\/([^/]+)$/, async (request, ctx, params) => {
388
+ const denied = blogDisabled(ctx);
389
+ if (denied) return denied;
390
+ const body = await readJson(request);
391
+ if (body instanceof Response) return body;
392
+ const parsed = import_core.updateBlogPostInputSchema.safeParse(body);
393
+ if (!parsed.success) {
394
+ return Response.json({ error: "Invalid blog post", details: parsed.error.flatten() }, { status: 400 });
395
+ }
396
+ const updated = await ctx.store.blog.update(ctx.scope, params[1], parsed.data);
397
+ if (!updated) return Response.json({ error: "Not found" }, { status: 404 });
398
+ return Response.json({ data: updated });
399
+ });
400
+ addRoute("DELETE", /^\/blog\/([^/]+)$/, async (_req, ctx, params) => {
401
+ const denied = blogDisabled(ctx);
402
+ if (denied) return denied;
403
+ const deleted = await ctx.store.blog.delete(ctx.scope, params[1]);
404
+ if (!deleted) return Response.json({ error: "Not found" }, { status: 404 });
405
+ return new Response(null, { status: 204 });
406
+ });
407
+ addRoute("GET", /^\/reports$/, async (_req, ctx) => {
408
+ const data = await ctx.store.reports.list(ctx.scope);
409
+ return Response.json({ data });
410
+ });
411
+ addRoute("POST", /^\/reports$/, async (request, ctx) => {
412
+ const body = await readJson(request);
413
+ if (body instanceof Response) return body;
414
+ const from = new Date(String(body.from));
415
+ const to = new Date(String(body.to));
416
+ const title = String(body.title ?? "Report");
417
+ const keywords = await ctx.store.keywords.list(ctx.scope);
418
+ const snapshots = await ctx.store.snapshots.listByRange({
419
+ tenantId: ctx.scope.tenantId,
420
+ projectId: ctx.scope.projectId,
421
+ from,
422
+ to
423
+ });
424
+ const audits = await ctx.store.audits.list(ctx.scope);
425
+ const reportData = (0, import_core.buildReport)({
426
+ tenantId: ctx.scope.tenantId,
427
+ projectId: ctx.scope.projectId,
428
+ title,
429
+ from,
430
+ to,
431
+ keywords,
432
+ snapshots,
433
+ audits
434
+ });
435
+ const report = await ctx.store.reports.create(reportData);
436
+ return Response.json({ data: report }, { status: 201 });
437
+ });
438
+ addRoute("GET", /^\/reports\/([^/]+)$/, async (_req, ctx, params) => {
439
+ const report = await ctx.store.reports.getById(ctx.scope, params[1]);
440
+ if (!report) return Response.json({ error: "Not found" }, { status: 404 });
441
+ return Response.json({ data: report });
442
+ });
443
+ addRoute("GET", /^\/dashboard$/, async (_req, ctx) => {
444
+ const config = await ctx.store.dashboard.get(ctx.scope);
445
+ return Response.json({ data: config ?? null });
446
+ });
447
+ addRoute("PUT", /^\/dashboard$/, async (request, ctx) => {
448
+ const body = await readJson(request);
449
+ if (body instanceof Response) return body;
450
+ const existing = await ctx.store.dashboard.get(ctx.scope);
451
+ const parsed = import_core.dashboardConfigSchema.safeParse({
452
+ ...body,
453
+ id: existing?.id ?? (0, import_node_crypto.randomUUID)(),
454
+ tenantId: ctx.scope.tenantId,
455
+ projectId: ctx.scope.projectId,
456
+ updatedAt: /* @__PURE__ */ new Date()
457
+ });
458
+ if (!parsed.success) {
459
+ return Response.json({ error: "Invalid dashboard", details: parsed.error.flatten() }, { status: 400 });
460
+ }
461
+ const saved = await ctx.store.dashboard.upsert(parsed.data);
462
+ return Response.json({ data: saved });
463
+ });
464
+ addRoute("POST", /^\/agent\/chat$/, async (request, ctx) => {
465
+ if (!ctx.agentModel) {
466
+ return Response.json({ error: "Agent model not configured" }, { status: 503 });
467
+ }
468
+ const body = await readJson(request);
469
+ if (body instanceof Response) return body;
470
+ const result = await (0, import_agent.streamAgentChat)({
471
+ store: ctx.store,
472
+ scope: ctx.scope,
473
+ model: ctx.agentModel,
474
+ messages: body.messages ?? []
475
+ });
476
+ return result.toTextStreamResponse();
477
+ });
478
+ addRoute("GET", /^\/sitemap\.xml$/, async (_req, ctx) => {
479
+ if (!ctx.config.siteFeatures.sitemap) {
480
+ return Response.json({ error: "Sitemap disabled" }, { status: 404 });
481
+ }
482
+ const projects = await ctx.store.projects.list(ctx.scope);
483
+ const domain = projects[0]?.domain ?? "example.com";
484
+ const baseUrl = domain.startsWith("http") ? domain : `https://${domain}`;
485
+ const xml = buildSitemapXml(ctx.config.sitemapRoutes, baseUrl);
486
+ return new Response(xml, {
487
+ headers: { "Content-Type": "application/xml; charset=utf-8" }
488
+ });
489
+ });
490
+ addRoute("GET", /^\/llms\.txt$/, async (_req, ctx) => {
491
+ if (!ctx.config.siteFeatures.llmsTxt) {
492
+ return Response.json({ error: "llms.txt disabled" }, { status: 404 });
493
+ }
494
+ const text = buildLlmsTxt(ctx.config);
495
+ return new Response(text, {
496
+ headers: { "Content-Type": "text/markdown; charset=utf-8" }
497
+ });
498
+ });
499
+ addRoute("GET", /^\/$/, async (request, ctx, _params, url) => {
500
+ if (!ctx.config.siteFeatures.markdownNegotiation) {
501
+ return Response.json({ ok: true, service: "rankmyseo" });
502
+ }
503
+ const html = `<!DOCTYPE html><html><head><title>RankMySEO</title></head><body><h1>RankMySEO</h1><p>SEO toolkit API</p></body></html>`;
504
+ const md = pageToMarkdown(url.pathname, "RankMySEO");
505
+ return withMarkdownNegotiation(request, html, md, url.pathname);
506
+ });
507
+ async function dispatchRoute(request, ctx) {
508
+ const url = new URL(request.url);
509
+ const method = request.method.toUpperCase();
510
+ const pathname = url.pathname.replace(/\/+$/, "") || "/";
511
+ for (const route of routes) {
512
+ if (route.method !== method) continue;
513
+ const match = pathname.match(route.pattern);
514
+ if (!match) continue;
515
+ const params = {};
516
+ match.slice(1).forEach((value, index) => {
517
+ params[String(index + 1)] = value;
518
+ });
519
+ return route.handler(request, ctx, params, url);
520
+ }
521
+ return null;
522
+ }
523
+
524
+ // src/handler.ts
525
+ var defaultConfig = (0, import_core2.defineConfig)({
526
+ databaseUrl: "sqlite://:memory:",
527
+ tenantId: "default",
528
+ projectId: "default",
529
+ dataSources: [{ provider: "fixture", default: true }],
530
+ schedule: { cron: "0 6 * * *", enabled: false },
531
+ siteFeatures: {
532
+ sitemap: true,
533
+ llmsTxt: true,
534
+ collector: true,
535
+ markdownNegotiation: true,
536
+ blog: false
537
+ },
538
+ sitemapRoutes: ["/"]
539
+ });
540
+ function createHandler(store, options = {}) {
541
+ const config = options.config ?? defaultConfig;
542
+ return async (request) => {
543
+ const url = new URL(request.url);
544
+ const pathname = url.pathname.replace(/\/+$/, "") || "/";
545
+ const sitePaths = ["/sitemap.xml", "/llms.txt", "/"];
546
+ const needsScope = !sitePaths.includes(pathname) || pathname === "/";
547
+ let scope = { tenantId: config.tenantId, projectId: config.projectId };
548
+ if (needsScope && pathname !== "/") {
549
+ const parsed = readScope(request);
550
+ if (parsed instanceof Response) return parsed;
551
+ scope = parsed;
552
+ } else if (request.headers.get("x-tenant-id")) {
553
+ const parsed = readScope(request);
554
+ if (!(parsed instanceof Response)) scope = parsed;
555
+ }
556
+ const response = await dispatchRoute(request, {
557
+ store,
558
+ scope,
559
+ config,
560
+ agentModel: options.agentModel
561
+ });
562
+ if (response) return response;
563
+ return Response.json({ error: "Not found" }, { status: 404 });
564
+ };
565
+ }
566
+ // Annotate the CommonJS export names for ESM import in node:
567
+ 0 && (module.exports = {
568
+ createHandler,
569
+ readScope
570
+ });
571
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/handler.ts","../src/routes.ts","../src/utils.ts"],"sourcesContent":["export { createHandler, readScope, type HandlerOptions } from \"./handler.js\";\nexport type { RequestScope } from \"./utils.js\";\n","import \"server-only\";\n\nimport type { LanguageModel } from \"ai\";\nimport {\n defineConfig,\n type RankMySeoConfig,\n type RankStore,\n} from \"@rankmyseo/core\";\nimport { dispatchRoute } from \"./routes.js\";\nimport { readScope } from \"./utils.js\";\n\nexport type { RequestScope } from \"./utils.js\";\nexport { readScope } from \"./utils.js\";\nexport {\n buildLlmsTxt,\n buildSitemapXml,\n pageToMarkdown,\n withMarkdownNegotiation,\n} from \"./utils.js\";\n\nexport interface HandlerOptions {\n config?: RankMySeoConfig;\n agentModel?: LanguageModel;\n}\n\nconst defaultConfig = defineConfig({\n databaseUrl: \"sqlite://:memory:\",\n tenantId: \"default\",\n projectId: \"default\",\n dataSources: [{ provider: \"fixture\", default: true }],\n schedule: { cron: \"0 6 * * *\", enabled: false },\n siteFeatures: {\n sitemap: true,\n llmsTxt: true,\n collector: true,\n markdownNegotiation: true,\n blog: false,\n },\n sitemapRoutes: [\"/\"],\n});\n\nexport function createHandler(store: RankStore, options: HandlerOptions = {}) {\n const config = options.config ?? defaultConfig;\n\n return async (request: Request): Promise<Response> => {\n const url = new URL(request.url);\n const pathname = url.pathname.replace(/\\/+$/, \"\") || \"/\";\n\n const sitePaths = [\"/sitemap.xml\", \"/llms.txt\", \"/\"];\n const needsScope =\n !sitePaths.includes(pathname) || pathname === \"/\";\n\n let scope = { tenantId: config.tenantId, projectId: config.projectId };\n if (needsScope && pathname !== \"/\") {\n const parsed = readScope(request);\n if (parsed instanceof Response) return parsed;\n scope = parsed;\n } else if (request.headers.get(\"x-tenant-id\")) {\n const parsed = readScope(request);\n if (!(parsed instanceof Response)) scope = parsed;\n }\n\n const response = await dispatchRoute(request, {\n store,\n scope,\n config,\n agentModel: options.agentModel,\n });\n\n if (response) return response;\n return Response.json({ error: \"Not found\" }, { status: 404 });\n };\n}\n","import \"server-only\";\n\nimport { randomUUID } from \"node:crypto\";\nimport type { LanguageModel } from \"ai\";\nimport {\n buildAuditRecommendations,\n buildBlogRecommendations,\n buildReport,\n createAuditInputSchema,\n createBlogPostInputSchema,\n createKeywordInputSchema,\n createRankSnapshotInputSchema,\n dashboardConfigSchema,\n extractPageSignals,\n generateMeta,\n normalizeHttpUrl,\n pageSignalsSchema,\n projectSchema,\n runAuditChecks,\n slugify,\n snapshotRangeQuerySchema,\n updateBlogPostInputSchema,\n type RankMySeoConfig,\n type RankStore,\n type TenantScope,\n} from \"@rankmyseo/core\";\nimport { streamAgentChat } from \"@rankmyseo/agent\";\nimport {\n buildLlmsTxt,\n buildSitemapXml,\n pageToMarkdown,\n readJson,\n withMarkdownNegotiation,\n} from \"./utils.js\";\n\nexport interface RouteContext {\n store: RankStore;\n scope: TenantScope;\n config: RankMySeoConfig;\n agentModel?: LanguageModel;\n}\n\ntype RouteHandler = (\n request: Request,\n ctx: RouteContext,\n params: Record<string, string>,\n url: URL,\n) => Promise<Response>;\n\nconst routes: Array<{\n method: string;\n pattern: RegExp;\n handler: RouteHandler;\n}> = [];\n\nfunction addRoute(\n method: string,\n pattern: RegExp,\n handler: RouteHandler,\n): void {\n routes.push({ method, pattern, handler });\n}\n\naddRoute(\"GET\", /^\\/projects$/, async (_req, ctx) => {\n const data = await ctx.store.projects.list(ctx.scope);\n return Response.json({ data });\n});\n\naddRoute(\"POST\", /^\\/projects$/, async (request, ctx) => {\n const body = await readJson<unknown>(request);\n if (body instanceof Response) return body;\n const parsed = projectSchema\n .omit({ createdAt: true, updatedAt: true })\n .safeParse(body);\n if (!parsed.success) {\n return Response.json({ error: \"Invalid project\", details: parsed.error.flatten() }, { status: 400 });\n }\n const project = await ctx.store.projects.create({\n ...parsed.data,\n tenantId: ctx.scope.tenantId,\n });\n return Response.json({ data: project }, { status: 201 });\n});\n\naddRoute(\"GET\", /^\\/projects\\/([^/]+)$/, async (_req, ctx, params) => {\n const project = await ctx.store.projects.getById(ctx.scope, params[1]!);\n if (!project) return Response.json({ error: \"Not found\" }, { status: 404 });\n return Response.json({ data: project });\n});\n\naddRoute(\"GET\", /^\\/keywords$/, async (_req, ctx) => {\n const data = await ctx.store.keywords.list(ctx.scope);\n return Response.json({ data });\n});\n\naddRoute(\"POST\", /^\\/keywords$/, async (request, ctx) => {\n const body = await readJson<unknown>(request);\n if (body instanceof Response) return body;\n const parsed = createKeywordInputSchema.safeParse({\n ...(body as Record<string, unknown>),\n tenantId: ctx.scope.tenantId,\n projectId: ctx.scope.projectId,\n });\n if (!parsed.success) {\n return Response.json({ error: \"Invalid keyword\", details: parsed.error.flatten() }, { status: 400 });\n }\n const keyword = await ctx.store.keywords.create(parsed.data);\n return Response.json({ data: keyword }, { status: 201 });\n});\n\naddRoute(\"GET\", /^\\/keywords\\/([^/]+)$/, async (_req, ctx, params) => {\n const keyword = await ctx.store.keywords.getById(ctx.scope, params[1]!);\n if (!keyword) return Response.json({ error: \"Not found\" }, { status: 404 });\n return Response.json({ data: keyword });\n});\n\naddRoute(\"DELETE\", /^\\/keywords\\/([^/]+)$/, async (_req, ctx, params) => {\n const deleted = await ctx.store.keywords.delete(ctx.scope, params[1]!);\n if (!deleted) return Response.json({ error: \"Not found\" }, { status: 404 });\n return new Response(null, { status: 204 });\n});\n\naddRoute(\"POST\", /^\\/snapshots$/, async (request, ctx) => {\n const body = await readJson<unknown>(request);\n if (body instanceof Response) return body;\n const parsed = createRankSnapshotInputSchema.safeParse({\n ...(body as Record<string, unknown>),\n tenantId: ctx.scope.tenantId,\n projectId: ctx.scope.projectId,\n });\n if (!parsed.success) {\n return Response.json({ error: \"Invalid snapshot\", details: parsed.error.flatten() }, { status: 400 });\n }\n const snapshot = await ctx.store.snapshots.append(parsed.data);\n return Response.json({ data: snapshot }, { status: 201 });\n});\n\naddRoute(\"GET\", /^\\/snapshots$/, async (_req, ctx, _params, url) => {\n const parsed = snapshotRangeQuerySchema.safeParse({\n tenantId: ctx.scope.tenantId,\n projectId: ctx.scope.projectId,\n keywordId: url.searchParams.get(\"keywordId\") ?? undefined,\n from: url.searchParams.get(\"from\"),\n to: url.searchParams.get(\"to\"),\n });\n if (!parsed.success) {\n return Response.json({ error: \"Invalid query\", details: parsed.error.flatten() }, { status: 400 });\n }\n const data = await ctx.store.snapshots.listByRange(parsed.data);\n return Response.json({ data });\n});\n\naddRoute(\"GET\", /^\\/audits$/, async (_req, ctx) => {\n const data = await ctx.store.audits.list(ctx.scope);\n return Response.json({ data });\n});\n\naddRoute(\"POST\", /^\\/audits$/, async (request, ctx) => {\n const body = await readJson<unknown>(request);\n if (body instanceof Response) return body;\n const parsed = createAuditInputSchema.safeParse({\n ...(body as Record<string, unknown>),\n tenantId: ctx.scope.tenantId,\n projectId: ctx.scope.projectId,\n });\n if (!parsed.success) {\n return Response.json({ error: \"Invalid audit\", details: parsed.error.flatten() }, { status: 400 });\n }\n const audit = await ctx.store.audits.create({\n ...parsed.data,\n id: randomUUID(),\n });\n return Response.json({ data: audit }, { status: 201 });\n});\n\naddRoute(\"GET\", /^\\/audits\\/([^/]+)$/, async (_req, ctx, params) => {\n const audit = await ctx.store.audits.getById(ctx.scope, params[1]!);\n if (!audit) return Response.json({ error: \"Not found\" }, { status: 404 });\n return Response.json({ data: audit });\n});\n\naddRoute(\"POST\", /^\\/collect$/, async (request, ctx) => {\n if (!ctx.config.siteFeatures.collector) {\n return Response.json({ error: \"Collector disabled\" }, { status: 403 });\n }\n const body = await readJson<unknown>(request);\n if (body instanceof Response) return body;\n const parsed = pageSignalsSchema.safeParse(body);\n if (!parsed.success) {\n return Response.json({ error: \"Invalid signals\", details: parsed.error.flatten() }, { status: 400 });\n }\n const { checks, score } = runAuditChecks(parsed.data);\n const audit = await ctx.store.audits.create({\n id: randomUUID(),\n tenantId: ctx.scope.tenantId,\n projectId: ctx.scope.projectId,\n url: parsed.data.url,\n score,\n checks,\n });\n return Response.json({ data: audit }, { status: 201 });\n});\n\naddRoute(\"POST\", /^\\/scan$/, async (request, ctx) => {\n const body = await readJson<{ url?: string }>(request);\n if (body instanceof Response) return body;\n\n let target: URL;\n try {\n target = normalizeHttpUrl(String(body.url ?? \"\"));\n } catch {\n return Response.json({ error: \"A valid url is required\" }, { status: 400 });\n }\n if (target.protocol !== \"http:\" && target.protocol !== \"https:\") {\n return Response.json({ error: \"Only http(s) URLs can be scanned\" }, { status: 400 });\n }\n\n let html: string;\n try {\n const res = await fetch(target.toString(), {\n headers: { \"user-agent\": \"RankMySEO-Scanner/1.0\" },\n redirect: \"follow\",\n });\n if (!res.ok) {\n return Response.json(\n { error: `Fetch failed with status ${res.status}` },\n { status: 502 },\n );\n }\n html = await res.text();\n } catch {\n return Response.json({ error: \"Could not fetch the target URL\" }, { status: 502 });\n }\n\n const signals = extractPageSignals(html, target.toString());\n const { checks, score } = runAuditChecks(signals);\n const audit = await ctx.store.audits.create({\n id: randomUUID(),\n tenantId: ctx.scope.tenantId,\n projectId: ctx.scope.projectId,\n url: target.toString(),\n score,\n checks,\n });\n const recommendations = buildAuditRecommendations(checks);\n\n return Response.json(\n { data: { audit, signals, recommendations } },\n { status: 201 },\n );\n});\n\naddRoute(\"POST\", /^\\/meta\\/generate$/, async (request, _ctx) => {\n const body = await readJson<{\n title?: string;\n content?: string;\n targetKeyword?: string;\n url?: string;\n siteName?: string;\n }>(request);\n if (body instanceof Response) return body;\n if (!body.title || !body.title.trim()) {\n return Response.json({ error: \"title is required\" }, { status: 400 });\n }\n\n const meta = generateMeta({\n title: body.title,\n content: body.content,\n targetKeyword: body.targetKeyword,\n url: body.url,\n siteName: body.siteName,\n });\n\n const { checks, score } = runAuditChecks({\n url: body.url && /^https?:\\/\\//.test(body.url) ? body.url : \"https://example.com\",\n title: meta.metaTitle,\n metaDescription: meta.metaDescription,\n canonical: meta.canonical && /^https?:\\/\\//.test(meta.canonical) ? meta.canonical : null,\n h1Count: 1,\n hasOgTags: true,\n hasJsonLd: true,\n });\n\n return Response.json({ data: { meta, checks, score } });\n});\n\nfunction blogDisabled(ctx: RouteContext): Response | null {\n if (!ctx.config.siteFeatures.blog) {\n return Response.json({ error: \"Blog module disabled\" }, { status: 403 });\n }\n return null;\n}\n\naddRoute(\"GET\", /^\\/blog$/, async (_req, ctx) => {\n const denied = blogDisabled(ctx);\n if (denied) return denied;\n const data = await ctx.store.blog.list(ctx.scope);\n return Response.json({ data });\n});\n\naddRoute(\"POST\", /^\\/blog$/, async (request, ctx) => {\n const denied = blogDisabled(ctx);\n if (denied) return denied;\n const body = await readJson<Record<string, unknown>>(request);\n if (body instanceof Response) return body;\n const parsed = createBlogPostInputSchema.safeParse({\n ...body,\n tenantId: ctx.scope.tenantId,\n projectId: ctx.scope.projectId,\n });\n if (!parsed.success) {\n return Response.json({ error: \"Invalid blog post\", details: parsed.error.flatten() }, { status: 400 });\n }\n\n const input = parsed.data;\n const slug = input.slug?.trim() || slugify(input.title);\n let { metaTitle, metaDescription } = input;\n if (!metaTitle.trim() || !metaDescription.trim()) {\n const meta = generateMeta({\n title: input.title,\n content: input.content,\n targetKeyword: input.targetKeyword,\n });\n metaTitle = metaTitle.trim() || meta.metaTitle;\n metaDescription = metaDescription.trim() || meta.metaDescription;\n }\n\n const post = await ctx.store.blog.create({\n id: randomUUID(),\n tenantId: ctx.scope.tenantId,\n projectId: ctx.scope.projectId,\n title: input.title,\n slug,\n content: input.content,\n targetKeyword: input.targetKeyword,\n intent: input.intent,\n metaTitle,\n metaDescription,\n status: input.status,\n });\n return Response.json({ data: post }, { status: 201 });\n});\n\naddRoute(\"GET\", /^\\/blog\\/([^/]+)$/, async (_req, ctx, params) => {\n const denied = blogDisabled(ctx);\n if (denied) return denied;\n const post = await ctx.store.blog.getById(ctx.scope, params[1]!);\n if (!post) return Response.json({ error: \"Not found\" }, { status: 404 });\n const recommendations = buildBlogRecommendations({\n intent: post.intent,\n targetKeyword: post.targetKeyword,\n metaTitle: post.metaTitle,\n metaDescription: post.metaDescription,\n content: post.content,\n });\n return Response.json({ data: post, recommendations });\n});\n\naddRoute(\"PUT\", /^\\/blog\\/([^/]+)$/, async (request, ctx, params) => {\n const denied = blogDisabled(ctx);\n if (denied) return denied;\n const body = await readJson<Record<string, unknown>>(request);\n if (body instanceof Response) return body;\n const parsed = updateBlogPostInputSchema.safeParse(body);\n if (!parsed.success) {\n return Response.json({ error: \"Invalid blog post\", details: parsed.error.flatten() }, { status: 400 });\n }\n const updated = await ctx.store.blog.update(ctx.scope, params[1]!, parsed.data);\n if (!updated) return Response.json({ error: \"Not found\" }, { status: 404 });\n return Response.json({ data: updated });\n});\n\naddRoute(\"DELETE\", /^\\/blog\\/([^/]+)$/, async (_req, ctx, params) => {\n const denied = blogDisabled(ctx);\n if (denied) return denied;\n const deleted = await ctx.store.blog.delete(ctx.scope, params[1]!);\n if (!deleted) return Response.json({ error: \"Not found\" }, { status: 404 });\n return new Response(null, { status: 204 });\n});\n\naddRoute(\"GET\", /^\\/reports$/, async (_req, ctx) => {\n const data = await ctx.store.reports.list(ctx.scope);\n return Response.json({ data });\n});\n\naddRoute(\"POST\", /^\\/reports$/, async (request, ctx) => {\n const body = await readJson<Record<string, unknown>>(request);\n if (body instanceof Response) return body;\n const from = new Date(String(body.from));\n const to = new Date(String(body.to));\n const title = String(body.title ?? \"Report\");\n const keywords = await ctx.store.keywords.list(ctx.scope);\n const snapshots = await ctx.store.snapshots.listByRange({\n tenantId: ctx.scope.tenantId,\n projectId: ctx.scope.projectId,\n from,\n to,\n });\n const audits = await ctx.store.audits.list(ctx.scope);\n const reportData = buildReport({\n tenantId: ctx.scope.tenantId,\n projectId: ctx.scope.projectId,\n title,\n from,\n to,\n keywords,\n snapshots,\n audits,\n });\n const report = await ctx.store.reports.create(reportData);\n return Response.json({ data: report }, { status: 201 });\n});\n\naddRoute(\"GET\", /^\\/reports\\/([^/]+)$/, async (_req, ctx, params) => {\n const report = await ctx.store.reports.getById(ctx.scope, params[1]!);\n if (!report) return Response.json({ error: \"Not found\" }, { status: 404 });\n return Response.json({ data: report });\n});\n\naddRoute(\"GET\", /^\\/dashboard$/, async (_req, ctx) => {\n const config = await ctx.store.dashboard.get(ctx.scope);\n return Response.json({ data: config ?? null });\n});\n\naddRoute(\"PUT\", /^\\/dashboard$/, async (request, ctx) => {\n const body = await readJson<unknown>(request);\n if (body instanceof Response) return body;\n const existing = await ctx.store.dashboard.get(ctx.scope);\n const parsed = dashboardConfigSchema.safeParse({\n ...(body as Record<string, unknown>),\n id: existing?.id ?? randomUUID(),\n tenantId: ctx.scope.tenantId,\n projectId: ctx.scope.projectId,\n updatedAt: new Date(),\n });\n if (!parsed.success) {\n return Response.json({ error: \"Invalid dashboard\", details: parsed.error.flatten() }, { status: 400 });\n }\n const saved = await ctx.store.dashboard.upsert(parsed.data);\n return Response.json({ data: saved });\n});\n\naddRoute(\"POST\", /^\\/agent\\/chat$/, async (request, ctx) => {\n if (!ctx.agentModel) {\n return Response.json({ error: \"Agent model not configured\" }, { status: 503 });\n }\n const body = await readJson<{ messages?: Array<{ role: \"user\" | \"assistant\" | \"system\"; content: string }> }>(request);\n if (body instanceof Response) return body;\n const result = await streamAgentChat({\n store: ctx.store,\n scope: ctx.scope,\n model: ctx.agentModel,\n messages: body.messages ?? [],\n });\n return result.toTextStreamResponse();\n});\n\naddRoute(\"GET\", /^\\/sitemap\\.xml$/, async (_req, ctx) => {\n if (!ctx.config.siteFeatures.sitemap) {\n return Response.json({ error: \"Sitemap disabled\" }, { status: 404 });\n }\n const projects = await ctx.store.projects.list(ctx.scope);\n const domain = projects[0]?.domain ?? \"example.com\";\n const baseUrl = domain.startsWith(\"http\") ? domain : `https://${domain}`;\n const xml = buildSitemapXml(ctx.config.sitemapRoutes, baseUrl);\n return new Response(xml, {\n headers: { \"Content-Type\": \"application/xml; charset=utf-8\" },\n });\n});\n\naddRoute(\"GET\", /^\\/llms\\.txt$/, async (_req, ctx) => {\n if (!ctx.config.siteFeatures.llmsTxt) {\n return Response.json({ error: \"llms.txt disabled\" }, { status: 404 });\n }\n const text = buildLlmsTxt(ctx.config);\n return new Response(text, {\n headers: { \"Content-Type\": \"text/markdown; charset=utf-8\" },\n });\n});\n\naddRoute(\"GET\", /^\\/$/, async (request, ctx, _params, url) => {\n if (!ctx.config.siteFeatures.markdownNegotiation) {\n return Response.json({ ok: true, service: \"rankmyseo\" });\n }\n const html = `<!DOCTYPE html><html><head><title>RankMySEO</title></head><body><h1>RankMySEO</h1><p>SEO toolkit API</p></body></html>`;\n const md = pageToMarkdown(url.pathname, \"RankMySEO\");\n return withMarkdownNegotiation(request, html, md, url.pathname);\n});\n\nexport async function dispatchRoute(\n request: Request,\n ctx: RouteContext,\n): Promise<Response | null> {\n const url = new URL(request.url);\n const method = request.method.toUpperCase();\n const pathname = url.pathname.replace(/\\/+$/, \"\") || \"/\";\n\n for (const route of routes) {\n if (route.method !== method) continue;\n const match = pathname.match(route.pattern);\n if (!match) continue;\n const params: Record<string, string> = {};\n match.slice(1).forEach((value, index) => {\n params[String(index + 1)] = value;\n });\n return route.handler(request, ctx, params, url);\n }\n\n return null;\n}\n","import type { RankMySeoConfig, TenantScope } from \"@rankmyseo/core\";\n\nexport interface RequestScope extends TenantScope {}\n\nexport function readScope(request: Request): RequestScope | Response {\n const tenantId = request.headers.get(\"x-tenant-id\");\n const projectId = request.headers.get(\"x-project-id\");\n\n if (!tenantId || !projectId) {\n return Response.json(\n { error: \"Missing or invalid x-tenant-id / x-project-id headers\" },\n { status: 400 },\n );\n }\n\n return { tenantId, projectId };\n}\n\nexport async function readJson<T>(request: Request): Promise<T | Response> {\n try {\n return (await request.json()) as T;\n } catch {\n return Response.json({ error: \"Invalid JSON body\" }, { status: 400 });\n }\n}\n\nexport function acceptsMarkdown(request: Request): boolean {\n const accept = request.headers.get(\"accept\") ?? \"\";\n return accept.includes(\"text/markdown\");\n}\n\nexport interface SiteFeatureContext {\n config: RankMySeoConfig;\n scope: TenantScope;\n pathname: string;\n}\n\nexport function buildSitemapXml(routes: string[], baseUrl: string): string {\n const urls = routes\n .map(\n (route) =>\n ` <url><loc>${baseUrl}${route === \"/\" ? \"\" : route}</loc></url>`,\n )\n .join(\"\\n\");\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\\n${urls}\\n</urlset>`;\n}\n\nexport function buildLlmsTxt(config: RankMySeoConfig): string {\n const name = config.llmsTxt?.projectName ?? \"RankMySEO Project\";\n const summary =\n config.llmsTxt?.summary ??\n \"SEO tracking, audits, and rank history for this site.\";\n const links = config.llmsTxt?.links ?? [\n { title: \"Documentation\", url: \"/docs.md\" },\n ];\n\n const linkBlock = links.map((l) => `- [${l.title}](${l.url})`).join(\"\\n\");\n return `# ${name}\\n\\n> ${summary}\\n\\n## Resources\\n\\n${linkBlock}\\n`;\n}\n\nexport function pageToMarkdown(pathname: string, title: string): string {\n return `# ${title}\\n\\nPath: \\`${pathname}\\`\\n\\nThis page is available as Markdown for AI agents.\\n`;\n}\n\nexport function withMarkdownNegotiation(\n request: Request,\n html: string,\n markdown: string,\n pathname: string,\n): Response {\n const wantsMarkdown = acceptsMarkdown(request);\n const accept = request.headers.get(\"accept\") ?? \"\";\n\n if (wantsMarkdown) {\n return new Response(markdown, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/markdown; charset=utf-8\",\n Vary: \"Accept\",\n Link: `<${pathname}?format=html>; rel=\"alternate\"; type=\"text/html\"`,\n },\n });\n }\n\n if (accept && !accept.includes(\"*/*\") && !accept.includes(\"text/html\")) {\n return Response.json(\n { error: \"Not acceptable\", supported: [\"text/html\", \"text/markdown\"] },\n { status: 406, headers: { Vary: \"Accept\" } },\n );\n }\n\n return new Response(html, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n Vary: \"Accept\",\n Link: `<${pathname}>; rel=\"alternate\"; type=\"text/markdown\"`,\n },\n });\n}\n"],"mappings":"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","names":["import_server_only","import_core","routes"]}
@@ -0,0 +1,14 @@
1
+ import { LanguageModel } from 'ai';
2
+ import { TenantScope, RankMySeoConfig, RankStore } from '@rankmyseo/core';
3
+
4
+ interface RequestScope extends TenantScope {
5
+ }
6
+ declare function readScope(request: Request): RequestScope | Response;
7
+
8
+ interface HandlerOptions {
9
+ config?: RankMySeoConfig;
10
+ agentModel?: LanguageModel;
11
+ }
12
+ declare function createHandler(store: RankStore, options?: HandlerOptions): (request: Request) => Promise<Response>;
13
+
14
+ export { type HandlerOptions, type RequestScope, createHandler, readScope };