@seolful/nextjs-connector 1.0.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.
Files changed (98) hide show
  1. package/bin/seolful-next.mjs +4 -0
  2. package/dist/api.d.ts +4 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +43 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/cli/handshake.d.ts +17 -0
  7. package/dist/cli/handshake.d.ts.map +1 -0
  8. package/dist/cli/handshake.js +30 -0
  9. package/dist/cli/handshake.js.map +1 -0
  10. package/dist/cli/init.d.ts +2 -0
  11. package/dist/cli/init.d.ts.map +1 -0
  12. package/dist/cli/init.js +173 -0
  13. package/dist/cli/init.js.map +1 -0
  14. package/dist/cli/scaffold.d.ts +6 -0
  15. package/dist/cli/scaffold.d.ts.map +1 -0
  16. package/dist/cli/scaffold.js +101 -0
  17. package/dist/cli/scaffold.js.map +1 -0
  18. package/dist/config.d.ts +5 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +44 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/crawler/analyze.d.ts +18 -0
  23. package/dist/crawler/analyze.d.ts.map +1 -0
  24. package/dist/crawler/analyze.js +114 -0
  25. package/dist/crawler/analyze.js.map +1 -0
  26. package/dist/crawler/crawl-service.d.ts +10 -0
  27. package/dist/crawler/crawl-service.d.ts.map +1 -0
  28. package/dist/crawler/crawl-service.js +79 -0
  29. package/dist/crawler/crawl-service.js.map +1 -0
  30. package/dist/crawler/discover.d.ts +5 -0
  31. package/dist/crawler/discover.d.ts.map +1 -0
  32. package/dist/crawler/discover.js +49 -0
  33. package/dist/crawler/discover.js.map +1 -0
  34. package/dist/handlers/audit-data.d.ts +3 -0
  35. package/dist/handlers/audit-data.d.ts.map +1 -0
  36. package/dist/handlers/audit-data.js +46 -0
  37. package/dist/handlers/audit-data.js.map +1 -0
  38. package/dist/handlers/crawl.d.ts +2 -0
  39. package/dist/handlers/crawl.d.ts.map +1 -0
  40. package/dist/handlers/crawl.js +11 -0
  41. package/dist/handlers/crawl.js.map +1 -0
  42. package/dist/handlers/demote-h1.d.ts +3 -0
  43. package/dist/handlers/demote-h1.d.ts.map +1 -0
  44. package/dist/handlers/demote-h1.js +28 -0
  45. package/dist/handlers/demote-h1.js.map +1 -0
  46. package/dist/handlers/update-ai-visibility.d.ts +3 -0
  47. package/dist/handlers/update-ai-visibility.d.ts.map +1 -0
  48. package/dist/handlers/update-ai-visibility.js +38 -0
  49. package/dist/handlers/update-ai-visibility.js.map +1 -0
  50. package/dist/handlers/update-seo.d.ts +3 -0
  51. package/dist/handlers/update-seo.d.ts.map +1 -0
  52. package/dist/handlers/update-seo.js +65 -0
  53. package/dist/handlers/update-seo.js.map +1 -0
  54. package/dist/helpers/generate-metadata.d.ts +3 -0
  55. package/dist/helpers/generate-metadata.d.ts.map +1 -0
  56. package/dist/helpers/generate-metadata.js +15 -0
  57. package/dist/helpers/generate-metadata.js.map +1 -0
  58. package/dist/helpers/get-page-seo.d.ts +3 -0
  59. package/dist/helpers/get-page-seo.d.ts.map +1 -0
  60. package/dist/helpers/get-page-seo.js +10 -0
  61. package/dist/helpers/get-page-seo.js.map +1 -0
  62. package/dist/helpers/seolful-h1.d.ts +10 -0
  63. package/dist/helpers/seolful-h1.d.ts.map +1 -0
  64. package/dist/helpers/seolful-h1.js +11 -0
  65. package/dist/helpers/seolful-h1.js.map +1 -0
  66. package/dist/helpers/seolful-image.d.ts +8 -0
  67. package/dist/helpers/seolful-image.d.ts.map +1 -0
  68. package/dist/helpers/seolful-image.js +8 -0
  69. package/dist/helpers/seolful-image.js.map +1 -0
  70. package/dist/helpers/seolful-schema.d.ts +6 -0
  71. package/dist/helpers/seolful-schema.d.ts.map +1 -0
  72. package/dist/helpers/seolful-schema.js +10 -0
  73. package/dist/helpers/seolful-schema.js.map +1 -0
  74. package/dist/index.d.ts +11 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +13 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/middleware/validate-token.d.ts +8 -0
  79. package/dist/middleware/validate-token.d.ts.map +1 -0
  80. package/dist/middleware/validate-token.js +29 -0
  81. package/dist/middleware/validate-token.js.map +1 -0
  82. package/dist/storage/file-adapter.d.ts +24 -0
  83. package/dist/storage/file-adapter.d.ts.map +1 -0
  84. package/dist/storage/file-adapter.js +118 -0
  85. package/dist/storage/file-adapter.js.map +1 -0
  86. package/dist/storage/index.d.ts +4 -0
  87. package/dist/storage/index.d.ts.map +1 -0
  88. package/dist/storage/index.js +13 -0
  89. package/dist/storage/index.js.map +1 -0
  90. package/dist/storage/types.d.ts +2 -0
  91. package/dist/storage/types.d.ts.map +1 -0
  92. package/dist/storage/types.js +2 -0
  93. package/dist/storage/types.js.map +1 -0
  94. package/dist/types.d.ts +60 -0
  95. package/dist/types.d.ts.map +1 -0
  96. package/dist/types.js +35 -0
  97. package/dist/types.js.map +1 -0
  98. package/package.json +61 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-token.d.ts","sourceRoot":"","sources":["../../src/middleware/validate-token.ts"],"names":[],"mappings":"AAGA,wBAAsB,aAAa,CACjC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CA+B5E"}
@@ -0,0 +1,29 @@
1
+ import bcrypt from 'bcryptjs';
2
+ import { getStorage } from '../storage/index.js';
3
+ export async function validateToken(headers) {
4
+ const token = headers.get('x-seolful-token');
5
+ const clientId = headers.get('x-seolful-client-id');
6
+ if (!token || !clientId) {
7
+ return { valid: false, status: 401, error: 'Unauthorized' };
8
+ }
9
+ // In Vercel/serverless, env vars are the source of truth for auth
10
+ const envClientId = process.env.SEOLFUL_CLIENT_ID;
11
+ const envToken = process.env.SEOLFUL_TOKEN;
12
+ if (envClientId && envToken) {
13
+ if (clientId === envClientId && token === envToken) {
14
+ return { valid: true };
15
+ }
16
+ return { valid: false, status: 401, error: 'Unauthorized' };
17
+ }
18
+ const storage = getStorage();
19
+ const connection = await storage.getConnection();
20
+ if (!connection || connection.clientId !== clientId) {
21
+ return { valid: false, status: 401, error: 'Unauthorized' };
22
+ }
23
+ const match = await bcrypt.compare(token, connection.tokenHash);
24
+ if (!match) {
25
+ return { valid: false, status: 401, error: 'Unauthorized' };
26
+ }
27
+ return { valid: true };
28
+ }
29
+ //# sourceMappingURL=validate-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-token.js","sourceRoot":"","sources":["../../src/middleware/validate-token.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,UAAU,CAAA;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAEhD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAgB;IAEhB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;IAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;IAEnD,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,CAAA;IAC7D,CAAC;IAED,kEAAkE;IAClE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAA;IACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAA;IAC1C,IAAI,WAAW,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,QAAQ,KAAK,WAAW,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;YACnD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;QACxB,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,CAAA;IAC7D,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAC5B,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,CAAA;IAEhD,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,CAAA;IAC7D,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;IAC/D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,CAAA;IAC7D,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AACxB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { StorageAdapter, SeolfulConnection, SeoPage } from '../types.js';
2
+ export declare class FileAdapter implements StorageAdapter {
3
+ private dir;
4
+ constructor(storageDir: string);
5
+ private path;
6
+ private readJson;
7
+ private writeJson;
8
+ getConnection(): Promise<SeolfulConnection | null>;
9
+ saveConnection(conn: SeolfulConnection): Promise<void>;
10
+ deleteConnection(): Promise<void>;
11
+ private loadPages;
12
+ private savePages;
13
+ getPageByUrl(url: string): Promise<SeoPage | null>;
14
+ getPageById(id: number): Promise<SeoPage | null>;
15
+ getAllPages(page: number, perPage: number): Promise<{
16
+ data: SeoPage[];
17
+ total: number;
18
+ }>;
19
+ upsertPage(page: Omit<SeoPage, 'id'>): Promise<SeoPage>;
20
+ upsertPages(pages: Array<Omit<SeoPage, 'id'>>): Promise<void>;
21
+ acquireLock(key: string, ttlMs: number): Promise<boolean>;
22
+ releaseLock(key: string): Promise<void>;
23
+ }
24
+ //# sourceMappingURL=file-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-adapter.d.ts","sourceRoot":"","sources":["../../src/storage/file-adapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAW7E,qBAAa,WAAY,YAAW,cAAc;IAChD,OAAO,CAAC,GAAG,CAAQ;gBAEP,UAAU,EAAE,MAAM;IAO9B,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,SAAS;IAWX,aAAa,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAMlD,cAAc,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAOvC,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,SAAS;IAIX,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAKlD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAKhD,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IASvF,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBvD,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB7D,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAazD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAK9C"}
@@ -0,0 +1,118 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, unlinkSync } from 'node:fs';
2
+ import { join, dirname } from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ export class FileAdapter {
5
+ dir;
6
+ constructor(storageDir) {
7
+ this.dir = storageDir;
8
+ if (!existsSync(this.dir)) {
9
+ mkdirSync(this.dir, { recursive: true });
10
+ }
11
+ }
12
+ path(file) {
13
+ return join(this.dir, file);
14
+ }
15
+ readJson(file, fallback) {
16
+ const p = this.path(file);
17
+ if (!existsSync(p))
18
+ return fallback;
19
+ try {
20
+ return JSON.parse(readFileSync(p, 'utf8'));
21
+ }
22
+ catch {
23
+ return fallback;
24
+ }
25
+ }
26
+ writeJson(file, data) {
27
+ const p = this.path(file);
28
+ const dir = dirname(p);
29
+ if (!existsSync(dir))
30
+ mkdirSync(dir, { recursive: true });
31
+ const tmp = p + '.' + randomUUID() + '.tmp';
32
+ writeFileSync(tmp, JSON.stringify(data, null, 2));
33
+ renameSync(tmp, p);
34
+ }
35
+ // -- Connection --
36
+ async getConnection() {
37
+ const conn = this.readJson('connection.json', null);
38
+ if (!conn?.clientId)
39
+ return null;
40
+ return conn;
41
+ }
42
+ async saveConnection(conn) {
43
+ this.writeJson('connection.json', conn);
44
+ }
45
+ async deleteConnection() {
46
+ const p = this.path('connection.json');
47
+ if (existsSync(p))
48
+ unlinkSync(p);
49
+ }
50
+ // -- Pages --
51
+ loadPages() {
52
+ return this.readJson('pages.json', { nextId: 1, pages: [] });
53
+ }
54
+ savePages(store) {
55
+ this.writeJson('pages.json', store);
56
+ }
57
+ async getPageByUrl(url) {
58
+ const store = this.loadPages();
59
+ return store.pages.find((p) => p.url === url) ?? null;
60
+ }
61
+ async getPageById(id) {
62
+ const store = this.loadPages();
63
+ return store.pages.find((p) => p.id === id) ?? null;
64
+ }
65
+ async getAllPages(page, perPage) {
66
+ const store = this.loadPages();
67
+ const start = (page - 1) * perPage;
68
+ return {
69
+ data: store.pages.slice(start, start + perPage),
70
+ total: store.pages.length,
71
+ };
72
+ }
73
+ async upsertPage(page) {
74
+ const store = this.loadPages();
75
+ const idx = store.pages.findIndex((p) => p.url === page.url);
76
+ if (idx >= 0) {
77
+ const existing = store.pages[idx];
78
+ store.pages[idx] = { ...existing, ...page, id: existing.id };
79
+ this.savePages(store);
80
+ return store.pages[idx];
81
+ }
82
+ const newPage = { ...page, id: store.nextId++ };
83
+ store.pages.push(newPage);
84
+ this.savePages(store);
85
+ return newPage;
86
+ }
87
+ async upsertPages(pages) {
88
+ const store = this.loadPages();
89
+ for (const page of pages) {
90
+ const idx = store.pages.findIndex((p) => p.url === page.url);
91
+ if (idx >= 0) {
92
+ const existing = store.pages[idx];
93
+ store.pages[idx] = { ...existing, ...page, id: existing.id };
94
+ }
95
+ else {
96
+ store.pages.push({ ...page, id: store.nextId++ });
97
+ }
98
+ }
99
+ this.savePages(store);
100
+ }
101
+ // -- Locks --
102
+ async acquireLock(key, ttlMs) {
103
+ const locks = this.readJson('locks.json', {});
104
+ const existing = locks[key];
105
+ if (existing && existing.expiresAt > Date.now()) {
106
+ return false;
107
+ }
108
+ locks[key] = { expiresAt: Date.now() + ttlMs };
109
+ this.writeJson('locks.json', locks);
110
+ return true;
111
+ }
112
+ async releaseLock(key) {
113
+ const locks = this.readJson('locks.json', {});
114
+ delete locks[key];
115
+ this.writeJson('locks.json', locks);
116
+ }
117
+ }
118
+ //# sourceMappingURL=file-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-adapter.js","sourceRoot":"","sources":["../../src/storage/file-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { StorageAdapter } from '../types.js';
2
+ export declare function getStorage(): StorageAdapter;
3
+ export declare function setStorage(adapter: StorageAdapter): void;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,wBAAgB,UAAU,IAAI,cAAc,CAK3C;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAExD"}
@@ -0,0 +1,13 @@
1
+ import { getStorageDir } from '../config.js';
2
+ import { FileAdapter } from './file-adapter.js';
3
+ let instance = null;
4
+ export function getStorage() {
5
+ if (!instance) {
6
+ instance = new FileAdapter(getStorageDir());
7
+ }
8
+ return instance;
9
+ }
10
+ export function setStorage(adapter) {
11
+ instance = adapter;
12
+ }
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAG/C,IAAI,QAAQ,GAA0B,IAAI,CAAA;AAE1C,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,WAAW,CAAC,aAAa,EAAE,CAAC,CAAA;IAC7C,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAuB;IAChD,QAAQ,GAAG,OAAO,CAAA;AACpB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export type { StorageAdapter, SeolfulConnection, SeoPage } from '../types.js';
2
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,60 @@
1
+ export interface SeolfulConfig {
2
+ appUrl: string;
3
+ siteUrl: string;
4
+ siteName: string;
5
+ connectionKey?: string;
6
+ storageDir?: string;
7
+ crawl?: {
8
+ urls?: string[];
9
+ sitemapUrl?: string;
10
+ useSitemap?: boolean;
11
+ delayMs?: number;
12
+ timeout?: number;
13
+ };
14
+ }
15
+ export interface SeolfulConnection {
16
+ clientId: string;
17
+ tokenHash: string;
18
+ siteUrl: string;
19
+ connectedAt: string;
20
+ }
21
+ export interface ImageAlt {
22
+ src: string;
23
+ alt: string;
24
+ missing: boolean;
25
+ }
26
+ export interface SeoPage {
27
+ id: number;
28
+ url: string;
29
+ slug: string;
30
+ title: string | null;
31
+ metaDescription: string | null;
32
+ h1: string | null;
33
+ h1Count: number;
34
+ h1Secondary: string | null;
35
+ demoteH1: boolean;
36
+ wordCount: number;
37
+ imageAlts: ImageAlt[];
38
+ internalLinkCount: number;
39
+ structuredData: object[];
40
+ noindex: boolean;
41
+ canonicalUrl: string | null;
42
+ crawledAt: string | null;
43
+ }
44
+ export interface StorageAdapter {
45
+ getConnection(): Promise<SeolfulConnection | null>;
46
+ saveConnection(conn: SeolfulConnection): Promise<void>;
47
+ deleteConnection(): Promise<void>;
48
+ getPageByUrl(url: string): Promise<SeoPage | null>;
49
+ getPageById(id: number): Promise<SeoPage | null>;
50
+ getAllPages(page: number, perPage: number): Promise<{
51
+ data: SeoPage[];
52
+ total: number;
53
+ }>;
54
+ upsertPage(page: Omit<SeoPage, 'id'>): Promise<SeoPage>;
55
+ upsertPages(pages: Array<Omit<SeoPage, 'id'>>): Promise<void>;
56
+ acquireLock(key: string, ttlMs: number): Promise<boolean>;
57
+ releaseLock(key: string): Promise<void>;
58
+ }
59
+ export declare function getPageRole(page: SeoPage): 'content' | 'legal' | 'utility';
60
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;QACf,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,UAAU,CAAC,EAAE,OAAO,CAAA;QACpB,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,OAAO,CAAC,EAAE,MAAM,CAAA;KACjB,CAAA;CACF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAA;IAClD,cAAc,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtD,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAEjC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IAClD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IAChD,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACvF,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE7D,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACzD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACxC;AAyBD,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAW1E"}
package/dist/types.js ADDED
@@ -0,0 +1,35 @@
1
+ const LEGAL_EXACT = new Set([
2
+ 'privacy', 'terms', 'tos', 'cookie', 'cookies', 'disclaimer',
3
+ 'legal', 'refund', 'copyright', 'cancellation',
4
+ ]);
5
+ const LEGAL_SUBSTRING = [
6
+ 'privacy-policy', 'cookie-policy', 'terms-of-service', 'terms-and-conditions',
7
+ 'return-policy', 'refund-policy', 'cancellation-policy',
8
+ 'gdpr', 'dmca', 'accessibility',
9
+ ];
10
+ const UTILITY_EXACT = new Set([
11
+ 'contact', 'contact-us', 'about', 'about-us', 'faq', 'faqs', 'search', 'sitemap',
12
+ ]);
13
+ const UTILITY_SUBSTRING = [
14
+ 'cart', 'checkout', 'my-account', 'wishlist', 'order-received',
15
+ 'login', 'log-in', 'register', 'sign-in', 'sign-up', 'signup',
16
+ 'lost-password', 'reset-password', 'forgot-password',
17
+ 'thank-you', 'thankyou',
18
+ 'coming-soon', 'maintenance', '404',
19
+ ];
20
+ export function getPageRole(page) {
21
+ const slug = (page.slug ?? '').toLowerCase().replace(/^\/|\/$/g, '');
22
+ const lastSegment = slug.split('/').pop() ?? '';
23
+ if (LEGAL_EXACT.has(lastSegment))
24
+ return 'legal';
25
+ if (LEGAL_SUBSTRING.some((p) => lastSegment.includes(p)))
26
+ return 'legal';
27
+ if (UTILITY_EXACT.has(lastSegment))
28
+ return 'utility';
29
+ if (UTILITY_SUBSTRING.some((p) => lastSegment.includes(p)))
30
+ return 'utility';
31
+ if ((page.wordCount ?? 0) < 50)
32
+ return 'utility';
33
+ return 'content';
34
+ }
35
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA8DA,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY;IAC5D,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc;CAC/C,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG;IACtB,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,sBAAsB;IAC7E,eAAe,EAAE,eAAe,EAAE,qBAAqB;IACvD,MAAM,EAAE,MAAM,EAAE,eAAe;CAChC,CAAA;AAED,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS;CACjF,CAAC,CAAA;AAEF,MAAM,iBAAiB,GAAG;IACxB,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB;IAC9D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ;IAC7D,eAAe,EAAE,gBAAgB,EAAE,iBAAiB;IACpD,WAAW,EAAE,UAAU;IACvB,aAAa,EAAE,aAAa,EAAE,KAAK;CACpC,CAAA;AAED,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IACpE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAA;IAE/C,IAAI,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC;QAAE,OAAO,OAAO,CAAA;IAChD,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,OAAO,CAAA;IACxE,IAAI,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC;QAAE,OAAO,SAAS,CAAA;IACpD,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAA;IAC5E,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,EAAE;QAAE,OAAO,SAAS,CAAA;IAEhD,OAAO,SAAS,CAAA;AAClB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@seolful/nextjs-connector",
3
+ "version": "1.0.0",
4
+ "description": "Seolful SEO connector for Next.js — self-sufficient site auditing, fix publishing, and metadata injection",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./api": {
14
+ "types": "./dist/api.d.ts",
15
+ "import": "./dist/api.js"
16
+ }
17
+ },
18
+ "bin": {
19
+ "seolful-next": "./bin/seolful-next.mjs"
20
+ },
21
+ "files": [
22
+ "dist/",
23
+ "bin/"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "dev": "tsc --watch"
28
+ },
29
+ "engines": {
30
+ "node": ">=18"
31
+ },
32
+ "peerDependencies": {
33
+ "next": ">=14.0.0",
34
+ "react": ">=18.0.0"
35
+ },
36
+ "dependencies": {
37
+ "bcryptjs": "^2.4.3",
38
+ "node-html-parser": "^6.1.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/bcryptjs": "^2.4.6",
42
+ "@types/node": "^20",
43
+ "@types/react": "^19",
44
+ "next": "^15.0.0",
45
+ "react": "^19.0.0",
46
+ "typescript": "^5"
47
+ },
48
+ "keywords": [
49
+ "seolful",
50
+ "seo",
51
+ "nextjs",
52
+ "next",
53
+ "audit",
54
+ "metadata",
55
+ "connector"
56
+ ],
57
+ "license": "MIT",
58
+ "publishConfig": {
59
+ "access": "public"
60
+ }
61
+ }