rankdeploy-middleware 1.1.0 → 1.2.1
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/{chunk-UQC64IMV.mjs → chunk-C43DAMVJ.mjs} +17 -5
- package/dist/chunk-C43DAMVJ.mjs.map +1 -0
- package/dist/express.js +19 -10
- package/dist/express.js.map +1 -1
- package/dist/express.mjs +5 -8
- package/dist/express.mjs.map +1 -1
- package/dist/index.js +15 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/netlify.js +20 -13
- package/dist/netlify.js.map +1 -1
- package/dist/netlify.mjs +6 -11
- package/dist/netlify.mjs.map +1 -1
- package/dist/next.js +19 -4
- package/dist/next.js.map +1 -1
- package/dist/next.mjs +5 -2
- package/dist/next.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-UQC64IMV.mjs.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
var RANKDEPLOY_API = "https://proxy.unikium.com";
|
|
3
|
+
var STATIC_EXTENSIONS = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;
|
|
3
4
|
var BOT_USER_AGENTS = [
|
|
4
5
|
"googlebot",
|
|
5
6
|
"bingbot",
|
|
@@ -30,9 +31,20 @@ var BOT_USER_AGENTS = [
|
|
|
30
31
|
"anthropic",
|
|
31
32
|
"claudebot",
|
|
32
33
|
"perplexitybot",
|
|
33
|
-
"cohere"
|
|
34
|
+
"cohere",
|
|
35
|
+
// SEO tools & OG checkers
|
|
36
|
+
"opengraph",
|
|
37
|
+
"metatags",
|
|
38
|
+
"preview",
|
|
39
|
+
"crawler",
|
|
40
|
+
"spider",
|
|
41
|
+
"fetch",
|
|
42
|
+
"curl",
|
|
43
|
+
"wget",
|
|
44
|
+
"python-requests",
|
|
45
|
+
"node-fetch",
|
|
46
|
+
"axios"
|
|
34
47
|
];
|
|
35
|
-
var STATIC_EXTENSIONS = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;
|
|
36
48
|
function isBot(userAgent, extraBots) {
|
|
37
49
|
const ua = userAgent.toLowerCase();
|
|
38
50
|
const allBots = extraBots ? [...BOT_USER_AGENTS, ...extraBots] : BOT_USER_AGENTS;
|
|
@@ -50,7 +62,7 @@ async function fetchPrerendered(url, userAgent, siteKey, apiUrl = RANKDEPLOY_API
|
|
|
50
62
|
const res = await fetch(
|
|
51
63
|
`${apiUrl}/render?url=${encodeURIComponent(url)}&key=${encodeURIComponent(siteKey)}`,
|
|
52
64
|
{
|
|
53
|
-
headers: { "User-Agent": userAgent },
|
|
65
|
+
headers: { "User-Agent": userAgent || "Googlebot/2.1" },
|
|
54
66
|
signal: AbortSignal.timeout(1e4)
|
|
55
67
|
}
|
|
56
68
|
);
|
|
@@ -65,11 +77,11 @@ async function fetchPrerendered(url, userAgent, siteKey, apiUrl = RANKDEPLOY_API
|
|
|
65
77
|
|
|
66
78
|
export {
|
|
67
79
|
RANKDEPLOY_API,
|
|
68
|
-
BOT_USER_AGENTS,
|
|
69
80
|
STATIC_EXTENSIONS,
|
|
81
|
+
BOT_USER_AGENTS,
|
|
70
82
|
isBot,
|
|
71
83
|
isStaticAsset,
|
|
72
84
|
isExcluded,
|
|
73
85
|
fetchPrerendered
|
|
74
86
|
};
|
|
75
|
-
//# sourceMappingURL=chunk-
|
|
87
|
+
//# sourceMappingURL=chunk-C43DAMVJ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * rankdeploy-middleware — Core SEO middleware engine\n *\n * Serves pre-rendered HTML with SEO overrides to all visitors.\n * The pre-rendered HTML is visually identical to the original page\n * but includes injected meta tags (title, description, canonical, og:tags).\n */\n\nexport const RANKDEPLOY_API = \"https://proxy.unikium.com\";\n\nexport const STATIC_EXTENSIONS = /\\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;\n\nexport interface RankDeployOptions {\n /** Your site key from Rank Deploy dashboard (required) */\n siteKey: string;\n /** Rank Deploy API URL. Default: https://proxy.unikium.com */\n apiUrl?: string;\n /** Paths to exclude from pre-rendering */\n excludePaths?: string[];\n /** Only serve to bots (default: false — serves to everyone for OG checker compatibility) */\n botsOnly?: boolean;\n /** Additional bot user agents to detect (used when botsOnly=true) */\n extraBots?: string[];\n}\n\nexport const BOT_USER_AGENTS = [\n \"googlebot\", \"bingbot\", \"yandexbot\", \"duckduckbot\", \"baiduspider\",\n \"slurp\", \"sogou\", \"exabot\",\n \"facebot\", \"facebookexternalhit\", \"twitterbot\", \"linkedinbot\",\n \"whatsapp\", \"telegrambot\", \"applebot\", \"discordbot\", \"slackbot\", \"pinterest\",\n \"screaming frog\", \"ahrefs\", \"semrush\", \"moz.com\", \"rogerbot\", \"dotbot\",\n \"gptbot\", \"chatgpt\", \"anthropic\", \"claudebot\", \"perplexitybot\", \"cohere\",\n // SEO tools & OG checkers\n \"opengraph\", \"metatags\", \"preview\", \"crawler\", \"spider\", \"fetch\",\n \"curl\", \"wget\", \"python-requests\", \"node-fetch\", \"axios\",\n];\n\nexport function isBot(userAgent: string, extraBots?: string[]): boolean {\n const ua = userAgent.toLowerCase();\n const allBots = extraBots ? [...BOT_USER_AGENTS, ...extraBots] : BOT_USER_AGENTS;\n return allBots.some((bot) => ua.includes(bot));\n}\n\nexport function isStaticAsset(pathname: string): boolean {\n return STATIC_EXTENSIONS.test(pathname);\n}\n\nexport function isExcluded(pathname: string, excludePaths?: string[]): boolean {\n if (!excludePaths) return false;\n return excludePaths.some((p) => pathname.startsWith(p));\n}\n\n/**\n * Fetch pre-rendered HTML from Rank Deploy API.\n */\nexport async function fetchPrerendered(\n url: string,\n userAgent: string,\n siteKey: string,\n apiUrl = RANKDEPLOY_API,\n): Promise<string | null> {\n try {\n const res = await fetch(\n `${apiUrl}/render?url=${encodeURIComponent(url)}&key=${encodeURIComponent(siteKey)}`,\n {\n headers: { \"User-Agent\": userAgent || \"Googlebot/2.1\" },\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (res.status === 200) {\n return res.text();\n }\n return null;\n } catch {\n return null;\n }\n}\n"],"mappings":";AAQO,IAAM,iBAAiB;AAEvB,IAAM,oBAAoB;AAe1B,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EAAa;AAAA,EAAW;AAAA,EAAa;AAAA,EAAe;AAAA,EACpD;AAAA,EAAS;AAAA,EAAS;AAAA,EAClB;AAAA,EAAW;AAAA,EAAuB;AAAA,EAAc;AAAA,EAChD;AAAA,EAAY;AAAA,EAAe;AAAA,EAAY;AAAA,EAAc;AAAA,EAAY;AAAA,EACjE;AAAA,EAAkB;AAAA,EAAU;AAAA,EAAW;AAAA,EAAW;AAAA,EAAY;AAAA,EAC9D;AAAA,EAAU;AAAA,EAAW;AAAA,EAAa;AAAA,EAAa;AAAA,EAAiB;AAAA;AAAA,EAEhE;AAAA,EAAa;AAAA,EAAY;AAAA,EAAW;AAAA,EAAW;AAAA,EAAU;AAAA,EACzD;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAmB;AAAA,EAAc;AACnD;AAEO,SAAS,MAAM,WAAmB,WAA+B;AACtE,QAAM,KAAK,UAAU,YAAY;AACjC,QAAM,UAAU,YAAY,CAAC,GAAG,iBAAiB,GAAG,SAAS,IAAI;AACjE,SAAO,QAAQ,KAAK,CAAC,QAAQ,GAAG,SAAS,GAAG,CAAC;AAC/C;AAEO,SAAS,cAAc,UAA2B;AACvD,SAAO,kBAAkB,KAAK,QAAQ;AACxC;AAEO,SAAS,WAAW,UAAkB,cAAkC;AAC7E,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO,aAAa,KAAK,CAAC,MAAM,SAAS,WAAW,CAAC,CAAC;AACxD;AAKA,eAAsB,iBACpB,KACA,WACA,SACA,SAAS,gBACe;AACxB,MAAI;AACF,UAAM,MAAM,MAAM;AAAA,MAChB,GAAG,MAAM,eAAe,mBAAmB,GAAG,CAAC,QAAQ,mBAAmB,OAAO,CAAC;AAAA,MAClF;AAAA,QACE,SAAS,EAAE,cAAc,aAAa,gBAAgB;AAAA,QACtD,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,KAAK;AACtB,aAAO,IAAI,KAAK;AAAA,IAClB;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
package/dist/express.js
CHANGED
|
@@ -27,6 +27,7 @@ module.exports = __toCommonJS(express_exports);
|
|
|
27
27
|
|
|
28
28
|
// src/index.ts
|
|
29
29
|
var RANKDEPLOY_API = "https://proxy.unikium.com";
|
|
30
|
+
var STATIC_EXTENSIONS = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;
|
|
30
31
|
var BOT_USER_AGENTS = [
|
|
31
32
|
"googlebot",
|
|
32
33
|
"bingbot",
|
|
@@ -57,9 +58,20 @@ var BOT_USER_AGENTS = [
|
|
|
57
58
|
"anthropic",
|
|
58
59
|
"claudebot",
|
|
59
60
|
"perplexitybot",
|
|
60
|
-
"cohere"
|
|
61
|
+
"cohere",
|
|
62
|
+
// SEO tools & OG checkers
|
|
63
|
+
"opengraph",
|
|
64
|
+
"metatags",
|
|
65
|
+
"preview",
|
|
66
|
+
"crawler",
|
|
67
|
+
"spider",
|
|
68
|
+
"fetch",
|
|
69
|
+
"curl",
|
|
70
|
+
"wget",
|
|
71
|
+
"python-requests",
|
|
72
|
+
"node-fetch",
|
|
73
|
+
"axios"
|
|
61
74
|
];
|
|
62
|
-
var STATIC_EXTENSIONS = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;
|
|
63
75
|
function isBot(userAgent, extraBots) {
|
|
64
76
|
const ua = userAgent.toLowerCase();
|
|
65
77
|
const allBots = extraBots ? [...BOT_USER_AGENTS, ...extraBots] : BOT_USER_AGENTS;
|
|
@@ -77,7 +89,7 @@ async function fetchPrerendered(url, userAgent, siteKey, apiUrl = RANKDEPLOY_API
|
|
|
77
89
|
const res = await fetch(
|
|
78
90
|
`${apiUrl}/render?url=${encodeURIComponent(url)}&key=${encodeURIComponent(siteKey)}`,
|
|
79
91
|
{
|
|
80
|
-
headers: { "User-Agent": userAgent },
|
|
92
|
+
headers: { "User-Agent": userAgent || "Googlebot/2.1" },
|
|
81
93
|
signal: AbortSignal.timeout(1e4)
|
|
82
94
|
}
|
|
83
95
|
);
|
|
@@ -91,23 +103,20 @@ async function fetchPrerendered(url, userAgent, siteKey, apiUrl = RANKDEPLOY_API
|
|
|
91
103
|
}
|
|
92
104
|
|
|
93
105
|
// src/express.ts
|
|
94
|
-
function rankdeploy(options
|
|
106
|
+
function rankdeploy(options) {
|
|
95
107
|
return async function middleware(req, res, next) {
|
|
96
108
|
const userAgent = req.headers["user-agent"] || req.get?.("user-agent") || "";
|
|
97
109
|
const pathname = req.url.split("?")[0];
|
|
98
|
-
if (
|
|
99
|
-
return next();
|
|
100
|
-
}
|
|
101
|
-
if (isStaticAsset(pathname)) {
|
|
110
|
+
if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {
|
|
102
111
|
return next();
|
|
103
112
|
}
|
|
104
|
-
if (
|
|
113
|
+
if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {
|
|
105
114
|
return next();
|
|
106
115
|
}
|
|
107
116
|
const protocol = req.protocol || "https";
|
|
108
117
|
const host = req.get?.("host") || req.headers.host || "";
|
|
109
118
|
const fullUrl = `${protocol}://${host}${req.url}`;
|
|
110
|
-
const html = await fetchPrerendered(fullUrl, userAgent, options.apiUrl);
|
|
119
|
+
const html = await fetchPrerendered(fullUrl, userAgent, options.siteKey, options.apiUrl);
|
|
111
120
|
if (html) {
|
|
112
121
|
res.set("Content-Type", "text/html; charset=utf-8");
|
|
113
122
|
res.set("X-Prerendered", "true");
|
package/dist/express.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/express.ts","../src/index.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"sources":["../src/express.ts","../src/index.ts"],"sourcesContent":["/**\n * rankdeploy-middleware/express — Express.js middleware\n */\n\nimport { isBot, isStaticAsset, isExcluded, fetchPrerendered, type RankDeployOptions } from \"./index\";\n\ninterface ExpressRequest {\n headers: Record<string, string | string[] | undefined>;\n url: string;\n protocol: string;\n get?(name: string): string | undefined;\n}\n\ninterface ExpressResponse {\n set(name: string, value: string): void;\n status(code: number): ExpressResponse;\n send(body: string): void;\n}\n\ntype NextFunction = () => void;\n\nexport function rankdeploy(options: RankDeployOptions) {\n return async function middleware(req: ExpressRequest, res: ExpressResponse, next: NextFunction) {\n const userAgent = (req.headers[\"user-agent\"] as string) || req.get?.(\"user-agent\") || \"\";\n const pathname = req.url.split(\"?\")[0];\n\n if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {\n return next();\n }\n\n if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {\n return next();\n }\n\n const protocol = req.protocol || \"https\";\n const host = req.get?.(\"host\") || req.headers.host || \"\";\n const fullUrl = `${protocol}://${host}${req.url}`;\n\n const html = await fetchPrerendered(fullUrl, userAgent, options.siteKey, options.apiUrl);\n\n if (html) {\n res.set(\"Content-Type\", \"text/html; charset=utf-8\");\n res.set(\"X-Prerendered\", \"true\");\n res.set(\"X-Powered-By\", \"Rank Deploy\");\n return res.status(200).send(html);\n }\n\n next();\n };\n}\n\nexport default rankdeploy;\n","/**\n * rankdeploy-middleware — Core SEO middleware engine\n *\n * Serves pre-rendered HTML with SEO overrides to all visitors.\n * The pre-rendered HTML is visually identical to the original page\n * but includes injected meta tags (title, description, canonical, og:tags).\n */\n\nexport const RANKDEPLOY_API = \"https://proxy.unikium.com\";\n\nexport const STATIC_EXTENSIONS = /\\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;\n\nexport interface RankDeployOptions {\n /** Your site key from Rank Deploy dashboard (required) */\n siteKey: string;\n /** Rank Deploy API URL. Default: https://proxy.unikium.com */\n apiUrl?: string;\n /** Paths to exclude from pre-rendering */\n excludePaths?: string[];\n /** Only serve to bots (default: false — serves to everyone for OG checker compatibility) */\n botsOnly?: boolean;\n /** Additional bot user agents to detect (used when botsOnly=true) */\n extraBots?: string[];\n}\n\nexport const BOT_USER_AGENTS = [\n \"googlebot\", \"bingbot\", \"yandexbot\", \"duckduckbot\", \"baiduspider\",\n \"slurp\", \"sogou\", \"exabot\",\n \"facebot\", \"facebookexternalhit\", \"twitterbot\", \"linkedinbot\",\n \"whatsapp\", \"telegrambot\", \"applebot\", \"discordbot\", \"slackbot\", \"pinterest\",\n \"screaming frog\", \"ahrefs\", \"semrush\", \"moz.com\", \"rogerbot\", \"dotbot\",\n \"gptbot\", \"chatgpt\", \"anthropic\", \"claudebot\", \"perplexitybot\", \"cohere\",\n // SEO tools & OG checkers\n \"opengraph\", \"metatags\", \"preview\", \"crawler\", \"spider\", \"fetch\",\n \"curl\", \"wget\", \"python-requests\", \"node-fetch\", \"axios\",\n];\n\nexport function isBot(userAgent: string, extraBots?: string[]): boolean {\n const ua = userAgent.toLowerCase();\n const allBots = extraBots ? [...BOT_USER_AGENTS, ...extraBots] : BOT_USER_AGENTS;\n return allBots.some((bot) => ua.includes(bot));\n}\n\nexport function isStaticAsset(pathname: string): boolean {\n return STATIC_EXTENSIONS.test(pathname);\n}\n\nexport function isExcluded(pathname: string, excludePaths?: string[]): boolean {\n if (!excludePaths) return false;\n return excludePaths.some((p) => pathname.startsWith(p));\n}\n\n/**\n * Fetch pre-rendered HTML from Rank Deploy API.\n */\nexport async function fetchPrerendered(\n url: string,\n userAgent: string,\n siteKey: string,\n apiUrl = RANKDEPLOY_API,\n): Promise<string | null> {\n try {\n const res = await fetch(\n `${apiUrl}/render?url=${encodeURIComponent(url)}&key=${encodeURIComponent(siteKey)}`,\n {\n headers: { \"User-Agent\": userAgent || \"Googlebot/2.1\" },\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (res.status === 200) {\n return res.text();\n }\n return null;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACQO,IAAM,iBAAiB;AAEvB,IAAM,oBAAoB;AAe1B,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EAAa;AAAA,EAAW;AAAA,EAAa;AAAA,EAAe;AAAA,EACpD;AAAA,EAAS;AAAA,EAAS;AAAA,EAClB;AAAA,EAAW;AAAA,EAAuB;AAAA,EAAc;AAAA,EAChD;AAAA,EAAY;AAAA,EAAe;AAAA,EAAY;AAAA,EAAc;AAAA,EAAY;AAAA,EACjE;AAAA,EAAkB;AAAA,EAAU;AAAA,EAAW;AAAA,EAAW;AAAA,EAAY;AAAA,EAC9D;AAAA,EAAU;AAAA,EAAW;AAAA,EAAa;AAAA,EAAa;AAAA,EAAiB;AAAA;AAAA,EAEhE;AAAA,EAAa;AAAA,EAAY;AAAA,EAAW;AAAA,EAAW;AAAA,EAAU;AAAA,EACzD;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAmB;AAAA,EAAc;AACnD;AAEO,SAAS,MAAM,WAAmB,WAA+B;AACtE,QAAM,KAAK,UAAU,YAAY;AACjC,QAAM,UAAU,YAAY,CAAC,GAAG,iBAAiB,GAAG,SAAS,IAAI;AACjE,SAAO,QAAQ,KAAK,CAAC,QAAQ,GAAG,SAAS,GAAG,CAAC;AAC/C;AAEO,SAAS,cAAc,UAA2B;AACvD,SAAO,kBAAkB,KAAK,QAAQ;AACxC;AAEO,SAAS,WAAW,UAAkB,cAAkC;AAC7E,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO,aAAa,KAAK,CAAC,MAAM,SAAS,WAAW,CAAC,CAAC;AACxD;AAKA,eAAsB,iBACpB,KACA,WACA,SACA,SAAS,gBACe;AACxB,MAAI;AACF,UAAM,MAAM,MAAM;AAAA,MAChB,GAAG,MAAM,eAAe,mBAAmB,GAAG,CAAC,QAAQ,mBAAmB,OAAO,CAAC;AAAA,MAClF;AAAA,QACE,SAAS,EAAE,cAAc,aAAa,gBAAgB;AAAA,QACtD,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,KAAK;AACtB,aAAO,IAAI,KAAK;AAAA,IAClB;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ADxDO,SAAS,WAAW,SAA4B;AACrD,SAAO,eAAe,WAAW,KAAqB,KAAsB,MAAoB;AAC9F,UAAM,YAAa,IAAI,QAAQ,YAAY,KAAgB,IAAI,MAAM,YAAY,KAAK;AACtF,UAAM,WAAW,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC;AAErC,QAAI,cAAc,QAAQ,KAAK,WAAW,UAAU,QAAQ,YAAY,GAAG;AACzE,aAAO,KAAK;AAAA,IACd;AAEA,QAAI,QAAQ,aAAa,SAAS,CAAC,MAAM,WAAW,QAAQ,SAAS,GAAG;AACtE,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,WAAW,IAAI,YAAY;AACjC,UAAM,OAAO,IAAI,MAAM,MAAM,KAAK,IAAI,QAAQ,QAAQ;AACtD,UAAM,UAAU,GAAG,QAAQ,MAAM,IAAI,GAAG,IAAI,GAAG;AAE/C,UAAM,OAAO,MAAM,iBAAiB,SAAS,WAAW,QAAQ,SAAS,QAAQ,MAAM;AAEvF,QAAI,MAAM;AACR,UAAI,IAAI,gBAAgB,0BAA0B;AAClD,UAAI,IAAI,iBAAiB,MAAM;AAC/B,UAAI,IAAI,gBAAgB,aAAa;AACrC,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,IAAI;AAAA,IAClC;AAEA,SAAK;AAAA,EACP;AACF;AAEA,IAAO,kBAAQ;","names":[]}
|
package/dist/express.mjs
CHANGED
|
@@ -3,26 +3,23 @@ import {
|
|
|
3
3
|
isBot,
|
|
4
4
|
isExcluded,
|
|
5
5
|
isStaticAsset
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-C43DAMVJ.mjs";
|
|
7
7
|
|
|
8
8
|
// src/express.ts
|
|
9
|
-
function rankdeploy(options
|
|
9
|
+
function rankdeploy(options) {
|
|
10
10
|
return async function middleware(req, res, next) {
|
|
11
11
|
const userAgent = req.headers["user-agent"] || req.get?.("user-agent") || "";
|
|
12
12
|
const pathname = req.url.split("?")[0];
|
|
13
|
-
if (
|
|
13
|
+
if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {
|
|
14
14
|
return next();
|
|
15
15
|
}
|
|
16
|
-
if (
|
|
17
|
-
return next();
|
|
18
|
-
}
|
|
19
|
-
if (isExcluded(pathname, options.excludePaths)) {
|
|
16
|
+
if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {
|
|
20
17
|
return next();
|
|
21
18
|
}
|
|
22
19
|
const protocol = req.protocol || "https";
|
|
23
20
|
const host = req.get?.("host") || req.headers.host || "";
|
|
24
21
|
const fullUrl = `${protocol}://${host}${req.url}`;
|
|
25
|
-
const html = await fetchPrerendered(fullUrl, userAgent, options.apiUrl);
|
|
22
|
+
const html = await fetchPrerendered(fullUrl, userAgent, options.siteKey, options.apiUrl);
|
|
26
23
|
if (html) {
|
|
27
24
|
res.set("Content-Type", "text/html; charset=utf-8");
|
|
28
25
|
res.set("X-Prerendered", "true");
|
package/dist/express.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/express.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"sources":["../src/express.ts"],"sourcesContent":["/**\n * rankdeploy-middleware/express — Express.js middleware\n */\n\nimport { isBot, isStaticAsset, isExcluded, fetchPrerendered, type RankDeployOptions } from \"./index\";\n\ninterface ExpressRequest {\n headers: Record<string, string | string[] | undefined>;\n url: string;\n protocol: string;\n get?(name: string): string | undefined;\n}\n\ninterface ExpressResponse {\n set(name: string, value: string): void;\n status(code: number): ExpressResponse;\n send(body: string): void;\n}\n\ntype NextFunction = () => void;\n\nexport function rankdeploy(options: RankDeployOptions) {\n return async function middleware(req: ExpressRequest, res: ExpressResponse, next: NextFunction) {\n const userAgent = (req.headers[\"user-agent\"] as string) || req.get?.(\"user-agent\") || \"\";\n const pathname = req.url.split(\"?\")[0];\n\n if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {\n return next();\n }\n\n if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {\n return next();\n }\n\n const protocol = req.protocol || \"https\";\n const host = req.get?.(\"host\") || req.headers.host || \"\";\n const fullUrl = `${protocol}://${host}${req.url}`;\n\n const html = await fetchPrerendered(fullUrl, userAgent, options.siteKey, options.apiUrl);\n\n if (html) {\n res.set(\"Content-Type\", \"text/html; charset=utf-8\");\n res.set(\"X-Prerendered\", \"true\");\n res.set(\"X-Powered-By\", \"Rank Deploy\");\n return res.status(200).send(html);\n }\n\n next();\n };\n}\n\nexport default rankdeploy;\n"],"mappings":";;;;;;;;AAqBO,SAAS,WAAW,SAA4B;AACrD,SAAO,eAAe,WAAW,KAAqB,KAAsB,MAAoB;AAC9F,UAAM,YAAa,IAAI,QAAQ,YAAY,KAAgB,IAAI,MAAM,YAAY,KAAK;AACtF,UAAM,WAAW,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC;AAErC,QAAI,cAAc,QAAQ,KAAK,WAAW,UAAU,QAAQ,YAAY,GAAG;AACzE,aAAO,KAAK;AAAA,IACd;AAEA,QAAI,QAAQ,aAAa,SAAS,CAAC,MAAM,WAAW,QAAQ,SAAS,GAAG;AACtE,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,WAAW,IAAI,YAAY;AACjC,UAAM,OAAO,IAAI,MAAM,MAAM,KAAK,IAAI,QAAQ,QAAQ;AACtD,UAAM,UAAU,GAAG,QAAQ,MAAM,IAAI,GAAG,IAAI,GAAG;AAE/C,UAAM,OAAO,MAAM,iBAAiB,SAAS,WAAW,QAAQ,SAAS,QAAQ,MAAM;AAEvF,QAAI,MAAM;AACR,UAAI,IAAI,gBAAgB,0BAA0B;AAClD,UAAI,IAAI,iBAAiB,MAAM;AAC/B,UAAI,IAAI,gBAAgB,aAAa;AACrC,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,IAAI;AAAA,IAClC;AAEA,SAAK;AAAA,EACP;AACF;AAEA,IAAO,kBAAQ;","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ __export(src_exports, {
|
|
|
30
30
|
});
|
|
31
31
|
module.exports = __toCommonJS(src_exports);
|
|
32
32
|
var RANKDEPLOY_API = "https://proxy.unikium.com";
|
|
33
|
+
var STATIC_EXTENSIONS = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;
|
|
33
34
|
var BOT_USER_AGENTS = [
|
|
34
35
|
"googlebot",
|
|
35
36
|
"bingbot",
|
|
@@ -60,9 +61,20 @@ var BOT_USER_AGENTS = [
|
|
|
60
61
|
"anthropic",
|
|
61
62
|
"claudebot",
|
|
62
63
|
"perplexitybot",
|
|
63
|
-
"cohere"
|
|
64
|
+
"cohere",
|
|
65
|
+
// SEO tools & OG checkers
|
|
66
|
+
"opengraph",
|
|
67
|
+
"metatags",
|
|
68
|
+
"preview",
|
|
69
|
+
"crawler",
|
|
70
|
+
"spider",
|
|
71
|
+
"fetch",
|
|
72
|
+
"curl",
|
|
73
|
+
"wget",
|
|
74
|
+
"python-requests",
|
|
75
|
+
"node-fetch",
|
|
76
|
+
"axios"
|
|
64
77
|
];
|
|
65
|
-
var STATIC_EXTENSIONS = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;
|
|
66
78
|
function isBot(userAgent, extraBots) {
|
|
67
79
|
const ua = userAgent.toLowerCase();
|
|
68
80
|
const allBots = extraBots ? [...BOT_USER_AGENTS, ...extraBots] : BOT_USER_AGENTS;
|
|
@@ -80,7 +92,7 @@ async function fetchPrerendered(url, userAgent, siteKey, apiUrl = RANKDEPLOY_API
|
|
|
80
92
|
const res = await fetch(
|
|
81
93
|
`${apiUrl}/render?url=${encodeURIComponent(url)}&key=${encodeURIComponent(siteKey)}`,
|
|
82
94
|
{
|
|
83
|
-
headers: { "User-Agent": userAgent },
|
|
95
|
+
headers: { "User-Agent": userAgent || "Googlebot/2.1" },
|
|
84
96
|
signal: AbortSignal.timeout(1e4)
|
|
85
97
|
}
|
|
86
98
|
);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * rankdeploy-middleware — Core SEO middleware engine\n *\n *
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * rankdeploy-middleware — Core SEO middleware engine\n *\n * Serves pre-rendered HTML with SEO overrides to all visitors.\n * The pre-rendered HTML is visually identical to the original page\n * but includes injected meta tags (title, description, canonical, og:tags).\n */\n\nexport const RANKDEPLOY_API = \"https://proxy.unikium.com\";\n\nexport const STATIC_EXTENSIONS = /\\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;\n\nexport interface RankDeployOptions {\n /** Your site key from Rank Deploy dashboard (required) */\n siteKey: string;\n /** Rank Deploy API URL. Default: https://proxy.unikium.com */\n apiUrl?: string;\n /** Paths to exclude from pre-rendering */\n excludePaths?: string[];\n /** Only serve to bots (default: false — serves to everyone for OG checker compatibility) */\n botsOnly?: boolean;\n /** Additional bot user agents to detect (used when botsOnly=true) */\n extraBots?: string[];\n}\n\nexport const BOT_USER_AGENTS = [\n \"googlebot\", \"bingbot\", \"yandexbot\", \"duckduckbot\", \"baiduspider\",\n \"slurp\", \"sogou\", \"exabot\",\n \"facebot\", \"facebookexternalhit\", \"twitterbot\", \"linkedinbot\",\n \"whatsapp\", \"telegrambot\", \"applebot\", \"discordbot\", \"slackbot\", \"pinterest\",\n \"screaming frog\", \"ahrefs\", \"semrush\", \"moz.com\", \"rogerbot\", \"dotbot\",\n \"gptbot\", \"chatgpt\", \"anthropic\", \"claudebot\", \"perplexitybot\", \"cohere\",\n // SEO tools & OG checkers\n \"opengraph\", \"metatags\", \"preview\", \"crawler\", \"spider\", \"fetch\",\n \"curl\", \"wget\", \"python-requests\", \"node-fetch\", \"axios\",\n];\n\nexport function isBot(userAgent: string, extraBots?: string[]): boolean {\n const ua = userAgent.toLowerCase();\n const allBots = extraBots ? [...BOT_USER_AGENTS, ...extraBots] : BOT_USER_AGENTS;\n return allBots.some((bot) => ua.includes(bot));\n}\n\nexport function isStaticAsset(pathname: string): boolean {\n return STATIC_EXTENSIONS.test(pathname);\n}\n\nexport function isExcluded(pathname: string, excludePaths?: string[]): boolean {\n if (!excludePaths) return false;\n return excludePaths.some((p) => pathname.startsWith(p));\n}\n\n/**\n * Fetch pre-rendered HTML from Rank Deploy API.\n */\nexport async function fetchPrerendered(\n url: string,\n userAgent: string,\n siteKey: string,\n apiUrl = RANKDEPLOY_API,\n): Promise<string | null> {\n try {\n const res = await fetch(\n `${apiUrl}/render?url=${encodeURIComponent(url)}&key=${encodeURIComponent(siteKey)}`,\n {\n headers: { \"User-Agent\": userAgent || \"Googlebot/2.1\" },\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (res.status === 200) {\n return res.text();\n }\n return null;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQO,IAAM,iBAAiB;AAEvB,IAAM,oBAAoB;AAe1B,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EAAa;AAAA,EAAW;AAAA,EAAa;AAAA,EAAe;AAAA,EACpD;AAAA,EAAS;AAAA,EAAS;AAAA,EAClB;AAAA,EAAW;AAAA,EAAuB;AAAA,EAAc;AAAA,EAChD;AAAA,EAAY;AAAA,EAAe;AAAA,EAAY;AAAA,EAAc;AAAA,EAAY;AAAA,EACjE;AAAA,EAAkB;AAAA,EAAU;AAAA,EAAW;AAAA,EAAW;AAAA,EAAY;AAAA,EAC9D;AAAA,EAAU;AAAA,EAAW;AAAA,EAAa;AAAA,EAAa;AAAA,EAAiB;AAAA;AAAA,EAEhE;AAAA,EAAa;AAAA,EAAY;AAAA,EAAW;AAAA,EAAW;AAAA,EAAU;AAAA,EACzD;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAmB;AAAA,EAAc;AACnD;AAEO,SAAS,MAAM,WAAmB,WAA+B;AACtE,QAAM,KAAK,UAAU,YAAY;AACjC,QAAM,UAAU,YAAY,CAAC,GAAG,iBAAiB,GAAG,SAAS,IAAI;AACjE,SAAO,QAAQ,KAAK,CAAC,QAAQ,GAAG,SAAS,GAAG,CAAC;AAC/C;AAEO,SAAS,cAAc,UAA2B;AACvD,SAAO,kBAAkB,KAAK,QAAQ;AACxC;AAEO,SAAS,WAAW,UAAkB,cAAkC;AAC7E,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO,aAAa,KAAK,CAAC,MAAM,SAAS,WAAW,CAAC,CAAC;AACxD;AAKA,eAAsB,iBACpB,KACA,WACA,SACA,SAAS,gBACe;AACxB,MAAI;AACF,UAAM,MAAM,MAAM;AAAA,MAChB,GAAG,MAAM,eAAe,mBAAmB,GAAG,CAAC,QAAQ,mBAAmB,OAAO,CAAC;AAAA,MAClF;AAAA,QACE,SAAS,EAAE,cAAc,aAAa,gBAAgB;AAAA,QACtD,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,KAAK;AACtB,aAAO,IAAI,KAAK;AAAA,IAClB;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
package/dist/index.mjs
CHANGED
package/dist/netlify.js
CHANGED
|
@@ -20,13 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/netlify.ts
|
|
21
21
|
var netlify_exports = {};
|
|
22
22
|
__export(netlify_exports, {
|
|
23
|
-
createHandler: () => createHandler
|
|
24
|
-
default: () => netlify_default
|
|
23
|
+
createHandler: () => createHandler
|
|
25
24
|
});
|
|
26
25
|
module.exports = __toCommonJS(netlify_exports);
|
|
27
26
|
|
|
28
27
|
// src/index.ts
|
|
29
28
|
var RANKDEPLOY_API = "https://proxy.unikium.com";
|
|
29
|
+
var STATIC_EXTENSIONS = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;
|
|
30
30
|
var BOT_USER_AGENTS = [
|
|
31
31
|
"googlebot",
|
|
32
32
|
"bingbot",
|
|
@@ -57,9 +57,20 @@ var BOT_USER_AGENTS = [
|
|
|
57
57
|
"anthropic",
|
|
58
58
|
"claudebot",
|
|
59
59
|
"perplexitybot",
|
|
60
|
-
"cohere"
|
|
60
|
+
"cohere",
|
|
61
|
+
// SEO tools & OG checkers
|
|
62
|
+
"opengraph",
|
|
63
|
+
"metatags",
|
|
64
|
+
"preview",
|
|
65
|
+
"crawler",
|
|
66
|
+
"spider",
|
|
67
|
+
"fetch",
|
|
68
|
+
"curl",
|
|
69
|
+
"wget",
|
|
70
|
+
"python-requests",
|
|
71
|
+
"node-fetch",
|
|
72
|
+
"axios"
|
|
61
73
|
];
|
|
62
|
-
var STATIC_EXTENSIONS = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;
|
|
63
74
|
function isBot(userAgent, extraBots) {
|
|
64
75
|
const ua = userAgent.toLowerCase();
|
|
65
76
|
const allBots = extraBots ? [...BOT_USER_AGENTS, ...extraBots] : BOT_USER_AGENTS;
|
|
@@ -77,7 +88,7 @@ async function fetchPrerendered(url, userAgent, siteKey, apiUrl = RANKDEPLOY_API
|
|
|
77
88
|
const res = await fetch(
|
|
78
89
|
`${apiUrl}/render?url=${encodeURIComponent(url)}&key=${encodeURIComponent(siteKey)}`,
|
|
79
90
|
{
|
|
80
|
-
headers: { "User-Agent": userAgent },
|
|
91
|
+
headers: { "User-Agent": userAgent || "Googlebot/2.1" },
|
|
81
92
|
signal: AbortSignal.timeout(1e4)
|
|
82
93
|
}
|
|
83
94
|
);
|
|
@@ -91,20 +102,17 @@ async function fetchPrerendered(url, userAgent, siteKey, apiUrl = RANKDEPLOY_API
|
|
|
91
102
|
}
|
|
92
103
|
|
|
93
104
|
// src/netlify.ts
|
|
94
|
-
function createHandler(options
|
|
105
|
+
function createHandler(options) {
|
|
95
106
|
return async function handler(request, context) {
|
|
96
107
|
const userAgent = request.headers.get("user-agent") || "";
|
|
97
108
|
const pathname = new URL(request.url).pathname;
|
|
98
|
-
if (
|
|
99
|
-
return context.next();
|
|
100
|
-
}
|
|
101
|
-
if (isStaticAsset(pathname)) {
|
|
109
|
+
if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {
|
|
102
110
|
return context.next();
|
|
103
111
|
}
|
|
104
|
-
if (
|
|
112
|
+
if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {
|
|
105
113
|
return context.next();
|
|
106
114
|
}
|
|
107
|
-
const html = await fetchPrerendered(request.url, userAgent, options.apiUrl);
|
|
115
|
+
const html = await fetchPrerendered(request.url, userAgent, options.siteKey, options.apiUrl);
|
|
108
116
|
if (html) {
|
|
109
117
|
return new Response(html, {
|
|
110
118
|
status: 200,
|
|
@@ -118,7 +126,6 @@ function createHandler(options = {}) {
|
|
|
118
126
|
return context.next();
|
|
119
127
|
};
|
|
120
128
|
}
|
|
121
|
-
var netlify_default = createHandler();
|
|
122
129
|
// Annotate the CommonJS export names for ESM import in node:
|
|
123
130
|
0 && (module.exports = {
|
|
124
131
|
createHandler
|
package/dist/netlify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/netlify.ts","../src/index.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"sources":["../src/netlify.ts","../src/index.ts"],"sourcesContent":["/**\n * rankdeploy-middleware/netlify — Netlify Edge Function middleware\n */\n\nimport { isBot, isStaticAsset, isExcluded, fetchPrerendered, type RankDeployOptions } from \"./index\";\n\ninterface NetlifyContext {\n next(): Promise<Response>;\n}\n\nexport function createHandler(options: RankDeployOptions) {\n return async function handler(request: Request, context: NetlifyContext) {\n const userAgent = request.headers.get(\"user-agent\") || \"\";\n const pathname = new URL(request.url).pathname;\n\n if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {\n return context.next();\n }\n\n if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {\n return context.next();\n }\n\n const html = await fetchPrerendered(request.url, userAgent, options.siteKey, options.apiUrl);\n\n if (html) {\n return new Response(html, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"X-Prerendered\": \"true\",\n \"X-Powered-By\": \"Rank Deploy\",\n },\n });\n }\n\n return context.next();\n };\n}\n","/**\n * rankdeploy-middleware — Core SEO middleware engine\n *\n * Serves pre-rendered HTML with SEO overrides to all visitors.\n * The pre-rendered HTML is visually identical to the original page\n * but includes injected meta tags (title, description, canonical, og:tags).\n */\n\nexport const RANKDEPLOY_API = \"https://proxy.unikium.com\";\n\nexport const STATIC_EXTENSIONS = /\\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;\n\nexport interface RankDeployOptions {\n /** Your site key from Rank Deploy dashboard (required) */\n siteKey: string;\n /** Rank Deploy API URL. Default: https://proxy.unikium.com */\n apiUrl?: string;\n /** Paths to exclude from pre-rendering */\n excludePaths?: string[];\n /** Only serve to bots (default: false — serves to everyone for OG checker compatibility) */\n botsOnly?: boolean;\n /** Additional bot user agents to detect (used when botsOnly=true) */\n extraBots?: string[];\n}\n\nexport const BOT_USER_AGENTS = [\n \"googlebot\", \"bingbot\", \"yandexbot\", \"duckduckbot\", \"baiduspider\",\n \"slurp\", \"sogou\", \"exabot\",\n \"facebot\", \"facebookexternalhit\", \"twitterbot\", \"linkedinbot\",\n \"whatsapp\", \"telegrambot\", \"applebot\", \"discordbot\", \"slackbot\", \"pinterest\",\n \"screaming frog\", \"ahrefs\", \"semrush\", \"moz.com\", \"rogerbot\", \"dotbot\",\n \"gptbot\", \"chatgpt\", \"anthropic\", \"claudebot\", \"perplexitybot\", \"cohere\",\n // SEO tools & OG checkers\n \"opengraph\", \"metatags\", \"preview\", \"crawler\", \"spider\", \"fetch\",\n \"curl\", \"wget\", \"python-requests\", \"node-fetch\", \"axios\",\n];\n\nexport function isBot(userAgent: string, extraBots?: string[]): boolean {\n const ua = userAgent.toLowerCase();\n const allBots = extraBots ? [...BOT_USER_AGENTS, ...extraBots] : BOT_USER_AGENTS;\n return allBots.some((bot) => ua.includes(bot));\n}\n\nexport function isStaticAsset(pathname: string): boolean {\n return STATIC_EXTENSIONS.test(pathname);\n}\n\nexport function isExcluded(pathname: string, excludePaths?: string[]): boolean {\n if (!excludePaths) return false;\n return excludePaths.some((p) => pathname.startsWith(p));\n}\n\n/**\n * Fetch pre-rendered HTML from Rank Deploy API.\n */\nexport async function fetchPrerendered(\n url: string,\n userAgent: string,\n siteKey: string,\n apiUrl = RANKDEPLOY_API,\n): Promise<string | null> {\n try {\n const res = await fetch(\n `${apiUrl}/render?url=${encodeURIComponent(url)}&key=${encodeURIComponent(siteKey)}`,\n {\n headers: { \"User-Agent\": userAgent || \"Googlebot/2.1\" },\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (res.status === 200) {\n return res.text();\n }\n return null;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACQO,IAAM,iBAAiB;AAEvB,IAAM,oBAAoB;AAe1B,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EAAa;AAAA,EAAW;AAAA,EAAa;AAAA,EAAe;AAAA,EACpD;AAAA,EAAS;AAAA,EAAS;AAAA,EAClB;AAAA,EAAW;AAAA,EAAuB;AAAA,EAAc;AAAA,EAChD;AAAA,EAAY;AAAA,EAAe;AAAA,EAAY;AAAA,EAAc;AAAA,EAAY;AAAA,EACjE;AAAA,EAAkB;AAAA,EAAU;AAAA,EAAW;AAAA,EAAW;AAAA,EAAY;AAAA,EAC9D;AAAA,EAAU;AAAA,EAAW;AAAA,EAAa;AAAA,EAAa;AAAA,EAAiB;AAAA;AAAA,EAEhE;AAAA,EAAa;AAAA,EAAY;AAAA,EAAW;AAAA,EAAW;AAAA,EAAU;AAAA,EACzD;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAmB;AAAA,EAAc;AACnD;AAEO,SAAS,MAAM,WAAmB,WAA+B;AACtE,QAAM,KAAK,UAAU,YAAY;AACjC,QAAM,UAAU,YAAY,CAAC,GAAG,iBAAiB,GAAG,SAAS,IAAI;AACjE,SAAO,QAAQ,KAAK,CAAC,QAAQ,GAAG,SAAS,GAAG,CAAC;AAC/C;AAEO,SAAS,cAAc,UAA2B;AACvD,SAAO,kBAAkB,KAAK,QAAQ;AACxC;AAEO,SAAS,WAAW,UAAkB,cAAkC;AAC7E,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO,aAAa,KAAK,CAAC,MAAM,SAAS,WAAW,CAAC,CAAC;AACxD;AAKA,eAAsB,iBACpB,KACA,WACA,SACA,SAAS,gBACe;AACxB,MAAI;AACF,UAAM,MAAM,MAAM;AAAA,MAChB,GAAG,MAAM,eAAe,mBAAmB,GAAG,CAAC,QAAQ,mBAAmB,OAAO,CAAC;AAAA,MAClF;AAAA,QACE,SAAS,EAAE,cAAc,aAAa,gBAAgB;AAAA,QACtD,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,KAAK;AACtB,aAAO,IAAI,KAAK;AAAA,IAClB;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ADnEO,SAAS,cAAc,SAA4B;AACxD,SAAO,eAAe,QAAQ,SAAkB,SAAyB;AACvE,UAAM,YAAY,QAAQ,QAAQ,IAAI,YAAY,KAAK;AACvD,UAAM,WAAW,IAAI,IAAI,QAAQ,GAAG,EAAE;AAEtC,QAAI,cAAc,QAAQ,KAAK,WAAW,UAAU,QAAQ,YAAY,GAAG;AACzE,aAAO,QAAQ,KAAK;AAAA,IACtB;AAEA,QAAI,QAAQ,aAAa,SAAS,CAAC,MAAM,WAAW,QAAQ,SAAS,GAAG;AACtE,aAAO,QAAQ,KAAK;AAAA,IACtB;AAEA,UAAM,OAAO,MAAM,iBAAiB,QAAQ,KAAK,WAAW,QAAQ,SAAS,QAAQ,MAAM;AAE3F,QAAI,MAAM;AACR,aAAO,IAAI,SAAS,MAAM;AAAA,QACxB,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB;AAAA,UACjB,gBAAgB;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO,QAAQ,KAAK;AAAA,EACtB;AACF;","names":[]}
|
package/dist/netlify.mjs
CHANGED
|
@@ -3,23 +3,20 @@ import {
|
|
|
3
3
|
isBot,
|
|
4
4
|
isExcluded,
|
|
5
5
|
isStaticAsset
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-C43DAMVJ.mjs";
|
|
7
7
|
|
|
8
8
|
// src/netlify.ts
|
|
9
|
-
function createHandler(options
|
|
9
|
+
function createHandler(options) {
|
|
10
10
|
return async function handler(request, context) {
|
|
11
11
|
const userAgent = request.headers.get("user-agent") || "";
|
|
12
12
|
const pathname = new URL(request.url).pathname;
|
|
13
|
-
if (
|
|
13
|
+
if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {
|
|
14
14
|
return context.next();
|
|
15
15
|
}
|
|
16
|
-
if (
|
|
16
|
+
if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {
|
|
17
17
|
return context.next();
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
return context.next();
|
|
21
|
-
}
|
|
22
|
-
const html = await fetchPrerendered(request.url, userAgent, options.apiUrl);
|
|
19
|
+
const html = await fetchPrerendered(request.url, userAgent, options.siteKey, options.apiUrl);
|
|
23
20
|
if (html) {
|
|
24
21
|
return new Response(html, {
|
|
25
22
|
status: 200,
|
|
@@ -33,9 +30,7 @@ function createHandler(options = {}) {
|
|
|
33
30
|
return context.next();
|
|
34
31
|
};
|
|
35
32
|
}
|
|
36
|
-
var netlify_default = createHandler();
|
|
37
33
|
export {
|
|
38
|
-
createHandler
|
|
39
|
-
netlify_default as default
|
|
34
|
+
createHandler
|
|
40
35
|
};
|
|
41
36
|
//# sourceMappingURL=netlify.mjs.map
|
package/dist/netlify.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/netlify.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"sources":["../src/netlify.ts"],"sourcesContent":["/**\n * rankdeploy-middleware/netlify — Netlify Edge Function middleware\n */\n\nimport { isBot, isStaticAsset, isExcluded, fetchPrerendered, type RankDeployOptions } from \"./index\";\n\ninterface NetlifyContext {\n next(): Promise<Response>;\n}\n\nexport function createHandler(options: RankDeployOptions) {\n return async function handler(request: Request, context: NetlifyContext) {\n const userAgent = request.headers.get(\"user-agent\") || \"\";\n const pathname = new URL(request.url).pathname;\n\n if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {\n return context.next();\n }\n\n if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {\n return context.next();\n }\n\n const html = await fetchPrerendered(request.url, userAgent, options.siteKey, options.apiUrl);\n\n if (html) {\n return new Response(html, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"X-Prerendered\": \"true\",\n \"X-Powered-By\": \"Rank Deploy\",\n },\n });\n }\n\n return context.next();\n };\n}\n"],"mappings":";;;;;;;;AAUO,SAAS,cAAc,SAA4B;AACxD,SAAO,eAAe,QAAQ,SAAkB,SAAyB;AACvE,UAAM,YAAY,QAAQ,QAAQ,IAAI,YAAY,KAAK;AACvD,UAAM,WAAW,IAAI,IAAI,QAAQ,GAAG,EAAE;AAEtC,QAAI,cAAc,QAAQ,KAAK,WAAW,UAAU,QAAQ,YAAY,GAAG;AACzE,aAAO,QAAQ,KAAK;AAAA,IACtB;AAEA,QAAI,QAAQ,aAAa,SAAS,CAAC,MAAM,WAAW,QAAQ,SAAS,GAAG;AACtE,aAAO,QAAQ,KAAK;AAAA,IACtB;AAEA,UAAM,OAAO,MAAM,iBAAiB,QAAQ,KAAK,WAAW,QAAQ,SAAS,QAAQ,MAAM;AAE3F,QAAI,MAAM;AACR,aAAO,IAAI,SAAS,MAAM;AAAA,QACxB,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB;AAAA,UACjB,gBAAgB;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO,QAAQ,KAAK;AAAA,EACtB;AACF;","names":[]}
|
package/dist/next.js
CHANGED
|
@@ -27,6 +27,7 @@ module.exports = __toCommonJS(next_exports);
|
|
|
27
27
|
|
|
28
28
|
// src/index.ts
|
|
29
29
|
var RANKDEPLOY_API = "https://proxy.unikium.com";
|
|
30
|
+
var STATIC_EXTENSIONS = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;
|
|
30
31
|
var BOT_USER_AGENTS = [
|
|
31
32
|
"googlebot",
|
|
32
33
|
"bingbot",
|
|
@@ -57,9 +58,20 @@ var BOT_USER_AGENTS = [
|
|
|
57
58
|
"anthropic",
|
|
58
59
|
"claudebot",
|
|
59
60
|
"perplexitybot",
|
|
60
|
-
"cohere"
|
|
61
|
+
"cohere",
|
|
62
|
+
// SEO tools & OG checkers
|
|
63
|
+
"opengraph",
|
|
64
|
+
"metatags",
|
|
65
|
+
"preview",
|
|
66
|
+
"crawler",
|
|
67
|
+
"spider",
|
|
68
|
+
"fetch",
|
|
69
|
+
"curl",
|
|
70
|
+
"wget",
|
|
71
|
+
"python-requests",
|
|
72
|
+
"node-fetch",
|
|
73
|
+
"axios"
|
|
61
74
|
];
|
|
62
|
-
var STATIC_EXTENSIONS = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;
|
|
63
75
|
function isBot(userAgent, extraBots) {
|
|
64
76
|
const ua = userAgent.toLowerCase();
|
|
65
77
|
const allBots = extraBots ? [...BOT_USER_AGENTS, ...extraBots] : BOT_USER_AGENTS;
|
|
@@ -77,7 +89,7 @@ async function fetchPrerendered(url, userAgent, siteKey, apiUrl = RANKDEPLOY_API
|
|
|
77
89
|
const res = await fetch(
|
|
78
90
|
`${apiUrl}/render?url=${encodeURIComponent(url)}&key=${encodeURIComponent(siteKey)}`,
|
|
79
91
|
{
|
|
80
|
-
headers: { "User-Agent": userAgent },
|
|
92
|
+
headers: { "User-Agent": userAgent || "Googlebot/2.1" },
|
|
81
93
|
signal: AbortSignal.timeout(1e4)
|
|
82
94
|
}
|
|
83
95
|
);
|
|
@@ -95,7 +107,10 @@ function createMiddleware(options) {
|
|
|
95
107
|
return async function middleware(request) {
|
|
96
108
|
const userAgent = request.headers.get("user-agent") || "";
|
|
97
109
|
const pathname = request.nextUrl.pathname;
|
|
98
|
-
if (
|
|
110
|
+
if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {
|
|
99
114
|
return;
|
|
100
115
|
}
|
|
101
116
|
const html = await fetchPrerendered(request.nextUrl.toString(), userAgent, options.siteKey, options.apiUrl);
|
package/dist/next.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/next.ts","../src/index.ts"],"sourcesContent":["/**\n * rankdeploy-middleware/next — Next.js / Vercel middleware\n *\n *
|
|
1
|
+
{"version":3,"sources":["../src/next.ts","../src/index.ts"],"sourcesContent":["/**\n * rankdeploy-middleware/next — Next.js / Vercel middleware\n *\n * Serves pre-rendered HTML with SEO overrides.\n * By default serves to ALL requests (so OG checkers work).\n * Set botsOnly: true to only serve to search engine bots.\n */\n\nimport { isBot, isStaticAsset, isExcluded, fetchPrerendered, type RankDeployOptions } from \"./index\";\n\ninterface NextMiddlewareRequest {\n headers: { get(name: string): string | null };\n nextUrl: { pathname: string; toString(): string };\n}\n\nexport function createMiddleware(options: RankDeployOptions) {\n return async function middleware(request: NextMiddlewareRequest) {\n const userAgent = request.headers.get(\"user-agent\") || \"\";\n const pathname = request.nextUrl.pathname;\n\n // Skip static assets and excluded paths\n if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {\n return;\n }\n\n // Only intercept bot requests (default behavior)\n // Set botsOnly: false to serve to everyone (not recommended — can cause loops)\n if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {\n return;\n }\n\n const html = await fetchPrerendered(request.nextUrl.toString(), userAgent, options.siteKey, options.apiUrl);\n\n if (html) {\n return new Response(html, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"X-Prerendered\": \"true\",\n \"X-Powered-By\": \"Rank Deploy\",\n },\n });\n }\n\n return;\n };\n}\n\nexport const config = {\n matcher: [\"/((?!api|_next/static|_next/image|favicon.ico).*)\"],\n};\n","/**\n * rankdeploy-middleware — Core SEO middleware engine\n *\n * Serves pre-rendered HTML with SEO overrides to all visitors.\n * The pre-rendered HTML is visually identical to the original page\n * but includes injected meta tags (title, description, canonical, og:tags).\n */\n\nexport const RANKDEPLOY_API = \"https://proxy.unikium.com\";\n\nexport const STATIC_EXTENSIONS = /\\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;\n\nexport interface RankDeployOptions {\n /** Your site key from Rank Deploy dashboard (required) */\n siteKey: string;\n /** Rank Deploy API URL. Default: https://proxy.unikium.com */\n apiUrl?: string;\n /** Paths to exclude from pre-rendering */\n excludePaths?: string[];\n /** Only serve to bots (default: false — serves to everyone for OG checker compatibility) */\n botsOnly?: boolean;\n /** Additional bot user agents to detect (used when botsOnly=true) */\n extraBots?: string[];\n}\n\nexport const BOT_USER_AGENTS = [\n \"googlebot\", \"bingbot\", \"yandexbot\", \"duckduckbot\", \"baiduspider\",\n \"slurp\", \"sogou\", \"exabot\",\n \"facebot\", \"facebookexternalhit\", \"twitterbot\", \"linkedinbot\",\n \"whatsapp\", \"telegrambot\", \"applebot\", \"discordbot\", \"slackbot\", \"pinterest\",\n \"screaming frog\", \"ahrefs\", \"semrush\", \"moz.com\", \"rogerbot\", \"dotbot\",\n \"gptbot\", \"chatgpt\", \"anthropic\", \"claudebot\", \"perplexitybot\", \"cohere\",\n // SEO tools & OG checkers\n \"opengraph\", \"metatags\", \"preview\", \"crawler\", \"spider\", \"fetch\",\n \"curl\", \"wget\", \"python-requests\", \"node-fetch\", \"axios\",\n];\n\nexport function isBot(userAgent: string, extraBots?: string[]): boolean {\n const ua = userAgent.toLowerCase();\n const allBots = extraBots ? [...BOT_USER_AGENTS, ...extraBots] : BOT_USER_AGENTS;\n return allBots.some((bot) => ua.includes(bot));\n}\n\nexport function isStaticAsset(pathname: string): boolean {\n return STATIC_EXTENSIONS.test(pathname);\n}\n\nexport function isExcluded(pathname: string, excludePaths?: string[]): boolean {\n if (!excludePaths) return false;\n return excludePaths.some((p) => pathname.startsWith(p));\n}\n\n/**\n * Fetch pre-rendered HTML from Rank Deploy API.\n */\nexport async function fetchPrerendered(\n url: string,\n userAgent: string,\n siteKey: string,\n apiUrl = RANKDEPLOY_API,\n): Promise<string | null> {\n try {\n const res = await fetch(\n `${apiUrl}/render?url=${encodeURIComponent(url)}&key=${encodeURIComponent(siteKey)}`,\n {\n headers: { \"User-Agent\": userAgent || \"Googlebot/2.1\" },\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (res.status === 200) {\n return res.text();\n }\n return null;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACQO,IAAM,iBAAiB;AAEvB,IAAM,oBAAoB;AAe1B,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EAAa;AAAA,EAAW;AAAA,EAAa;AAAA,EAAe;AAAA,EACpD;AAAA,EAAS;AAAA,EAAS;AAAA,EAClB;AAAA,EAAW;AAAA,EAAuB;AAAA,EAAc;AAAA,EAChD;AAAA,EAAY;AAAA,EAAe;AAAA,EAAY;AAAA,EAAc;AAAA,EAAY;AAAA,EACjE;AAAA,EAAkB;AAAA,EAAU;AAAA,EAAW;AAAA,EAAW;AAAA,EAAY;AAAA,EAC9D;AAAA,EAAU;AAAA,EAAW;AAAA,EAAa;AAAA,EAAa;AAAA,EAAiB;AAAA;AAAA,EAEhE;AAAA,EAAa;AAAA,EAAY;AAAA,EAAW;AAAA,EAAW;AAAA,EAAU;AAAA,EACzD;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAmB;AAAA,EAAc;AACnD;AAEO,SAAS,MAAM,WAAmB,WAA+B;AACtE,QAAM,KAAK,UAAU,YAAY;AACjC,QAAM,UAAU,YAAY,CAAC,GAAG,iBAAiB,GAAG,SAAS,IAAI;AACjE,SAAO,QAAQ,KAAK,CAAC,QAAQ,GAAG,SAAS,GAAG,CAAC;AAC/C;AAEO,SAAS,cAAc,UAA2B;AACvD,SAAO,kBAAkB,KAAK,QAAQ;AACxC;AAEO,SAAS,WAAW,UAAkB,cAAkC;AAC7E,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO,aAAa,KAAK,CAAC,MAAM,SAAS,WAAW,CAAC,CAAC;AACxD;AAKA,eAAsB,iBACpB,KACA,WACA,SACA,SAAS,gBACe;AACxB,MAAI;AACF,UAAM,MAAM,MAAM;AAAA,MAChB,GAAG,MAAM,eAAe,mBAAmB,GAAG,CAAC,QAAQ,mBAAmB,OAAO,CAAC;AAAA,MAClF;AAAA,QACE,SAAS,EAAE,cAAc,aAAa,gBAAgB;AAAA,QACtD,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,KAAK;AACtB,aAAO,IAAI,KAAK;AAAA,IAClB;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AD9DO,SAAS,iBAAiB,SAA4B;AAC3D,SAAO,eAAe,WAAW,SAAgC;AAC/D,UAAM,YAAY,QAAQ,QAAQ,IAAI,YAAY,KAAK;AACvD,UAAM,WAAW,QAAQ,QAAQ;AAGjC,QAAI,cAAc,QAAQ,KAAK,WAAW,UAAU,QAAQ,YAAY,GAAG;AACzE;AAAA,IACF;AAIA,QAAI,QAAQ,aAAa,SAAS,CAAC,MAAM,WAAW,QAAQ,SAAS,GAAG;AACtE;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,iBAAiB,QAAQ,QAAQ,SAAS,GAAG,WAAW,QAAQ,SAAS,QAAQ,MAAM;AAE1G,QAAI,MAAM;AACR,aAAO,IAAI,SAAS,MAAM;AAAA,QACxB,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB;AAAA,UACjB,gBAAgB;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH;AAEA;AAAA,EACF;AACF;AAEO,IAAM,SAAS;AAAA,EACpB,SAAS,CAAC,mDAAmD;AAC/D;","names":[]}
|
package/dist/next.mjs
CHANGED
|
@@ -3,14 +3,17 @@ import {
|
|
|
3
3
|
isBot,
|
|
4
4
|
isExcluded,
|
|
5
5
|
isStaticAsset
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-C43DAMVJ.mjs";
|
|
7
7
|
|
|
8
8
|
// src/next.ts
|
|
9
9
|
function createMiddleware(options) {
|
|
10
10
|
return async function middleware(request) {
|
|
11
11
|
const userAgent = request.headers.get("user-agent") || "";
|
|
12
12
|
const pathname = request.nextUrl.pathname;
|
|
13
|
-
if (
|
|
13
|
+
if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {
|
|
14
17
|
return;
|
|
15
18
|
}
|
|
16
19
|
const html = await fetchPrerendered(request.nextUrl.toString(), userAgent, options.siteKey, options.apiUrl);
|
package/dist/next.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/next.ts"],"sourcesContent":["/**\n * rankdeploy-middleware/next — Next.js / Vercel middleware\n *\n *
|
|
1
|
+
{"version":3,"sources":["../src/next.ts"],"sourcesContent":["/**\n * rankdeploy-middleware/next — Next.js / Vercel middleware\n *\n * Serves pre-rendered HTML with SEO overrides.\n * By default serves to ALL requests (so OG checkers work).\n * Set botsOnly: true to only serve to search engine bots.\n */\n\nimport { isBot, isStaticAsset, isExcluded, fetchPrerendered, type RankDeployOptions } from \"./index\";\n\ninterface NextMiddlewareRequest {\n headers: { get(name: string): string | null };\n nextUrl: { pathname: string; toString(): string };\n}\n\nexport function createMiddleware(options: RankDeployOptions) {\n return async function middleware(request: NextMiddlewareRequest) {\n const userAgent = request.headers.get(\"user-agent\") || \"\";\n const pathname = request.nextUrl.pathname;\n\n // Skip static assets and excluded paths\n if (isStaticAsset(pathname) || isExcluded(pathname, options.excludePaths)) {\n return;\n }\n\n // Only intercept bot requests (default behavior)\n // Set botsOnly: false to serve to everyone (not recommended — can cause loops)\n if (options.botsOnly !== false && !isBot(userAgent, options.extraBots)) {\n return;\n }\n\n const html = await fetchPrerendered(request.nextUrl.toString(), userAgent, options.siteKey, options.apiUrl);\n\n if (html) {\n return new Response(html, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"X-Prerendered\": \"true\",\n \"X-Powered-By\": \"Rank Deploy\",\n },\n });\n }\n\n return;\n };\n}\n\nexport const config = {\n matcher: [\"/((?!api|_next/static|_next/image|favicon.ico).*)\"],\n};\n"],"mappings":";;;;;;;;AAeO,SAAS,iBAAiB,SAA4B;AAC3D,SAAO,eAAe,WAAW,SAAgC;AAC/D,UAAM,YAAY,QAAQ,QAAQ,IAAI,YAAY,KAAK;AACvD,UAAM,WAAW,QAAQ,QAAQ;AAGjC,QAAI,cAAc,QAAQ,KAAK,WAAW,UAAU,QAAQ,YAAY,GAAG;AACzE;AAAA,IACF;AAIA,QAAI,QAAQ,aAAa,SAAS,CAAC,MAAM,WAAW,QAAQ,SAAS,GAAG;AACtE;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,iBAAiB,QAAQ,QAAQ,SAAS,GAAG,WAAW,QAAQ,SAAS,QAAQ,MAAM;AAE1G,QAAI,MAAM;AACR,aAAO,IAAI,SAAS,MAAM;AAAA,QACxB,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB;AAAA,UACjB,gBAAgB;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH;AAEA;AAAA,EACF;AACF;AAEO,IAAM,SAAS;AAAA,EACpB,SAAS,CAAC,mDAAmD;AAC/D;","names":[]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * rankdeploy-middleware — Core SEO middleware engine\n *\n * Detects search engine bots and serves pre-rendered HTML from Rank Deploy API.\n * Human visitors are not affected.\n *\n * Usage:\n * import { createMiddleware } from 'rankdeploy-middleware/next'\n * export const middleware = createMiddleware({ siteKey: 'your-site-key' })\n */\n\nexport const RANKDEPLOY_API = \"https://proxy.unikium.com\";\n\nexport const BOT_USER_AGENTS = [\n \"googlebot\", \"bingbot\", \"yandexbot\", \"duckduckbot\", \"baiduspider\",\n \"slurp\", \"sogou\", \"exabot\",\n \"facebot\", \"facebookexternalhit\", \"twitterbot\", \"linkedinbot\",\n \"whatsapp\", \"telegrambot\", \"applebot\", \"discordbot\", \"slackbot\", \"pinterest\",\n \"screaming frog\", \"ahrefs\", \"semrush\", \"moz.com\", \"rogerbot\", \"dotbot\",\n \"gptbot\", \"chatgpt\", \"anthropic\", \"claudebot\", \"perplexitybot\", \"cohere\",\n];\n\nexport const STATIC_EXTENSIONS = /\\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|json|xml|txt|mp4|webm|pdf|zip)$/i;\n\nexport interface RankDeployOptions {\n /** Your site key from Rank Deploy dashboard (required) */\n siteKey: string;\n /** Rank Deploy API URL. Default: https://proxy.unikium.com */\n apiUrl?: string;\n /** Additional bot user agents to detect */\n extraBots?: string[];\n /** Paths to exclude from pre-rendering */\n excludePaths?: string[];\n}\n\nexport function isBot(userAgent: string, extraBots?: string[]): boolean {\n const ua = userAgent.toLowerCase();\n const allBots = extraBots ? [...BOT_USER_AGENTS, ...extraBots] : BOT_USER_AGENTS;\n return allBots.some((bot) => ua.includes(bot));\n}\n\nexport function isStaticAsset(pathname: string): boolean {\n return STATIC_EXTENSIONS.test(pathname);\n}\n\nexport function isExcluded(pathname: string, excludePaths?: string[]): boolean {\n if (!excludePaths) return false;\n return excludePaths.some((p) => pathname.startsWith(p));\n}\n\n/**\n * Fetch pre-rendered HTML from Rank Deploy API.\n */\nexport async function fetchPrerendered(\n url: string,\n userAgent: string,\n siteKey: string,\n apiUrl = RANKDEPLOY_API,\n): Promise<string | null> {\n try {\n const res = await fetch(\n `${apiUrl}/render?url=${encodeURIComponent(url)}&key=${encodeURIComponent(siteKey)}`,\n {\n headers: { \"User-Agent\": userAgent },\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (res.status === 200) {\n return res.text();\n }\n return null;\n } catch {\n return null;\n }\n}\n"],"mappings":";AAWO,IAAM,iBAAiB;AAEvB,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EAAa;AAAA,EAAW;AAAA,EAAa;AAAA,EAAe;AAAA,EACpD;AAAA,EAAS;AAAA,EAAS;AAAA,EAClB;AAAA,EAAW;AAAA,EAAuB;AAAA,EAAc;AAAA,EAChD;AAAA,EAAY;AAAA,EAAe;AAAA,EAAY;AAAA,EAAc;AAAA,EAAY;AAAA,EACjE;AAAA,EAAkB;AAAA,EAAU;AAAA,EAAW;AAAA,EAAW;AAAA,EAAY;AAAA,EAC9D;AAAA,EAAU;AAAA,EAAW;AAAA,EAAa;AAAA,EAAa;AAAA,EAAiB;AAClE;AAEO,IAAM,oBAAoB;AAa1B,SAAS,MAAM,WAAmB,WAA+B;AACtE,QAAM,KAAK,UAAU,YAAY;AACjC,QAAM,UAAU,YAAY,CAAC,GAAG,iBAAiB,GAAG,SAAS,IAAI;AACjE,SAAO,QAAQ,KAAK,CAAC,QAAQ,GAAG,SAAS,GAAG,CAAC;AAC/C;AAEO,SAAS,cAAc,UAA2B;AACvD,SAAO,kBAAkB,KAAK,QAAQ;AACxC;AAEO,SAAS,WAAW,UAAkB,cAAkC;AAC7E,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO,aAAa,KAAK,CAAC,MAAM,SAAS,WAAW,CAAC,CAAC;AACxD;AAKA,eAAsB,iBACpB,KACA,WACA,SACA,SAAS,gBACe;AACxB,MAAI;AACF,UAAM,MAAM,MAAM;AAAA,MAChB,GAAG,MAAM,eAAe,mBAAmB,GAAG,CAAC,QAAQ,mBAAmB,OAAO,CAAC;AAAA,MAClF;AAAA,QACE,SAAS,EAAE,cAAc,UAAU;AAAA,QACnC,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,KAAK;AACtB,aAAO,IAAI,KAAK;AAAA,IAClB;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|