@sonordev/site-kit 2.2.8 → 2.2.9
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/SitemapSync-3ISOAVDT.mjs +4 -0
- package/dist/{SitemapSync-XGKC63TH.mjs.map → SitemapSync-3ISOAVDT.mjs.map} +1 -1
- package/dist/SitemapSync-4Y65CMUK.js +13 -0
- package/dist/{SitemapSync-TPA3GTGU.js.map → SitemapSync-4Y65CMUK.js.map} +1 -1
- package/dist/{chunk-F4VAOBJM.mjs → chunk-DV2BURIN.mjs} +3 -3
- package/dist/{chunk-F4VAOBJM.mjs.map → chunk-DV2BURIN.mjs.map} +1 -1
- package/dist/{chunk-DPO3ZPFK.js → chunk-GVDPTXN3.js} +3 -3
- package/dist/{chunk-DPO3ZPFK.js.map → chunk-GVDPTXN3.js.map} +1 -1
- package/dist/{chunk-UHMM22HX.js → chunk-JNXQX2R6.js} +7 -3
- package/dist/chunk-JNXQX2R6.js.map +1 -0
- package/dist/{chunk-PU5ULVL5.mjs → chunk-ZUCVEQZB.mjs} +7 -3
- package/dist/chunk-ZUCVEQZB.mjs.map +1 -0
- package/dist/layout/client.js +2 -2
- package/dist/layout/client.mjs +1 -1
- package/dist/layout/index.js +2 -2
- package/dist/layout/index.mjs +1 -1
- package/dist/seo/client.js +2 -2
- package/dist/seo/client.mjs +1 -1
- package/dist/seo/index.js +2 -2
- package/dist/seo/index.mjs +1 -1
- package/dist/sitemap/index.js +11 -7
- package/dist/sitemap/index.js.map +1 -1
- package/dist/sitemap/index.mjs +11 -7
- package/dist/sitemap/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/SitemapSync-TPA3GTGU.js +0 -13
- package/dist/SitemapSync-XGKC63TH.mjs +0 -4
- package/dist/chunk-PU5ULVL5.mjs.map +0 -1
- package/dist/chunk-UHMM22HX.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"SitemapSync-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"SitemapSync-3ISOAVDT.mjs"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkJNXQX2R6_js = require('./chunk-JNXQX2R6.js');
|
|
4
|
+
require('./chunk-ZSMWDLMK.js');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Object.defineProperty(exports, "SitemapSync", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function () { return chunkJNXQX2R6_js.SitemapSync; }
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=SitemapSync-4Y65CMUK.js.map
|
|
13
|
+
//# sourceMappingURL=SitemapSync-4Y65CMUK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"SitemapSync-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"SitemapSync-4Y65CMUK.js"}
|
|
@@ -63,7 +63,7 @@ var LazySignalBridge = dynamic(
|
|
|
63
63
|
{ ssr: false }
|
|
64
64
|
);
|
|
65
65
|
var LazySitemapSync = dynamic(
|
|
66
|
-
() => import('./SitemapSync-
|
|
66
|
+
() => import('./SitemapSync-3ISOAVDT.mjs').then((m) => ({ default: m.SitemapSync })),
|
|
67
67
|
{ ssr: false }
|
|
68
68
|
);
|
|
69
69
|
function SiteKitClientProviders({
|
|
@@ -150,5 +150,5 @@ function SiteKitClientProviders({
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
export { SiteKitClientProviders };
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
154
|
-
//# sourceMappingURL=chunk-
|
|
153
|
+
//# sourceMappingURL=chunk-DV2BURIN.mjs.map
|
|
154
|
+
//# sourceMappingURL=chunk-DV2BURIN.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/identity.tsx","../src/layout/SiteKitClientProviders.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-F4VAOBJM.mjs","sourcesContent":["/**\n * @sonordev/site-kit/shared — Unified Identity Management\n *\n * Single source of truth for visitor and session IDs across\n * Analytics, Signal, and Engage modules.\n *\n * Storage keys:\n * localStorage: _sk_vid (visitor ID, persists ~forever)\n * sessionStorage: _sk_sid (session ID, per-tab)\n * sessionStorage: _sk_stime (last activity timestamp for timeout)\n *\n * Storage keys are prefixed with `_sk_*`.\n */\n\n'use client'\n\nimport React, { createContext, useContext, useMemo } from 'react'\n\n// ============================================\n// ID Generation\n// ============================================\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID()\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)\n })\n}\n\n/**\n * Get or create a persistent visitor ID.\n * Stored in localStorage under `_sk_vid`.\n */\nexport function getOrCreateVisitorId(): string {\n if (typeof window === 'undefined') return ''\n\n let visitorId = localStorage.getItem('_sk_vid')\n\n if (!visitorId) {\n visitorId = generateId()\n }\n\n localStorage.setItem('_sk_vid', visitorId)\n return visitorId\n}\n\n/**\n * Get or create a session ID with inactivity timeout.\n * Stored in sessionStorage under `_sk_sid`.\n * Session expires after `timeoutMinutes` of inactivity (default 30).\n */\nexport function getOrCreateSessionId(timeoutMinutes = 30): string {\n if (typeof window === 'undefined') return ''\n\n const now = Date.now()\n const timeoutMs = timeoutMinutes * 60 * 1000\n\n const existingSession = sessionStorage.getItem('_sk_sid')\n const lastActivity = sessionStorage.getItem('_sk_stime')\n\n if (existingSession && lastActivity) {\n const elapsed = now - parseInt(lastActivity, 10)\n if (elapsed < timeoutMs) {\n // Session still active — refresh timestamp\n sessionStorage.setItem('_sk_stime', now.toString())\n sessionStorage.setItem('_sk_sid', existingSession)\n return existingSession\n }\n }\n\n // New session (first visit or timeout expired)\n const newSession = generateId()\n sessionStorage.setItem('_sk_sid', newSession)\n sessionStorage.setItem('_sk_stime', now.toString())\n return newSession\n}\n\n// ============================================\n// React Context\n// ============================================\n\nexport interface SiteKitIdentity {\n visitorId: string\n sessionId: string\n}\n\nconst SiteKitIdentityContext = createContext<SiteKitIdentity | null>(null)\n\nexport interface SiteKitIdentityProviderProps {\n visitorId: string\n sessionId: string\n children: React.ReactNode\n}\n\nexport function SiteKitIdentityProvider({\n visitorId,\n sessionId,\n children,\n}: SiteKitIdentityProviderProps) {\n const value = useMemo(() => ({ visitorId, sessionId }), [visitorId, sessionId])\n\n return (\n <SiteKitIdentityContext.Provider value={value}>\n {children}\n </SiteKitIdentityContext.Provider>\n )\n}\n\n/**\n * Access the shared visitor/session identity.\n * Falls back to generating IDs directly if used outside of SiteKitIdentityProvider.\n */\nexport function useSiteKitIdentity(): SiteKitIdentity {\n const context = useContext(SiteKitIdentityContext)\n if (context) return context\n\n // Fallback for standalone usage outside SiteKitLayout\n return {\n visitorId: getOrCreateVisitorId(),\n sessionId: getOrCreateSessionId(),\n }\n}\n","'use client'\n\n/**\n * SiteKitClientProviders — Client Island\n *\n * This is the 'use client' boundary for SiteKitLayout.\n * It composes the client-only providers (Analytics, Engage, Signal, SitemapSync)\n * that require React context or browser APIs.\n *\n * All modules are lazy-loaded via next/dynamic so their JS is only downloaded\n * when the feature is enabled — keeping the initial bundle lightweight.\n *\n * All modules share a unified identity (visitor ID + session ID) via\n * SiteKitIdentityProvider, ensuring consistent tracking across Analytics,\n * Signal, and Engage.\n *\n * Props are passed down from the server-side SiteKitLayout component,\n * which reads env vars at render time.\n */\n\nimport React, { Suspense, useCallback, useRef, useState, type ReactNode } from 'react'\nimport dynamic from 'next/dynamic'\nimport {\n SiteKitIdentityProvider,\n getOrCreateVisitorId,\n getOrCreateSessionId,\n} from '../shared/identity'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\n// Lazy-load feature modules — JS only downloaded when feature is enabled\nconst LazyAnalyticsProvider = dynamic(\n () => import('../analytics/AnalyticsProvider').then(m => ({ default: m.AnalyticsProvider })),\n { ssr: false },\n)\nconst LazyEngageWidget = dynamic(\n () => import('../engage/EngageWidget').then(m => ({ default: m.EngageWidget })),\n { ssr: false },\n)\nconst LazySignalBridge = dynamic(\n () => import('../signal/SignalBridge').then(m => ({ default: m.SignalBridge })),\n { ssr: false },\n)\nconst LazySitemapSync = dynamic(\n () => import('../SitemapSync').then(m => ({ default: m.SitemapSync })),\n { ssr: false },\n)\n\nexport interface SiteKitClientProvidersProps {\n children: ReactNode\n apiKey: string\n apiUrl: string\n projectId?: string\n analytics?: boolean | AnalyticsConfig\n engage?: boolean | EngageConfig\n signal?: boolean | SignalConfig\n sitemapSync?: boolean\n debug?: boolean\n}\n\nexport function SiteKitClientProviders({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n debug = false,\n}: SiteKitClientProvidersProps) {\n // Set window globals for modules that still read from them\n if (typeof window !== 'undefined') {\n ;(window as any).__SITE_KIT_API_URL__ = apiUrl\n ;(window as any).__SITE_KIT_API_KEY__ = apiKey\n ;(window as any).__SITE_KIT_DEBUG__ = debug\n }\n\n // Unified identity — single source of truth for all modules\n const [visitorId] = useState(() => getOrCreateVisitorId())\n const [sessionId] = useState(() => getOrCreateSessionId())\n\n // Page metadata bridge: Analytics writes, Signal reads\n const pageMetadataRef = useRef<Record<string, unknown> | null>(null)\n const onPageMetadata = useCallback((metadata: Record<string, unknown>) => {\n pageMetadataRef.current = metadata\n }, [])\n\n let content = <>{children}</>\n\n // Wrap with SignalBridge if enabled\n if (signal) {\n const signalConfig: SignalConfig =\n typeof signal === 'object' ? signal : {}\n content = (\n <LazySignalBridge\n enabled\n realtime={signalConfig.realtime !== false}\n experiments={signalConfig.experiments !== false}\n behaviorTracking={signalConfig.behaviorTracking !== false}\n visitorId={visitorId}\n sessionId={sessionId}\n pageMetadataRef={pageMetadataRef}\n >\n {content}\n </LazySignalBridge>\n )\n }\n\n // Wrap with Analytics if enabled\n if (analytics) {\n const analyticsConfig: AnalyticsConfig =\n typeof analytics === 'object' ? analytics : {}\n content = (\n <Suspense fallback={null}>\n <LazyAnalyticsProvider\n apiUrl={apiUrl}\n apiKey={apiKey}\n trackPageViews={analyticsConfig.trackPageViews !== false}\n trackWebVitals={analyticsConfig.trackWebVitals !== false}\n trackScrollDepth={analyticsConfig.trackScrollDepth !== false}\n trackClicks={analyticsConfig.trackClicks !== false}\n debug={debug}\n externalVisitorId={visitorId}\n externalSessionId={sessionId}\n onPageMetadata={onPageMetadata}\n >\n {content}\n </LazyAnalyticsProvider>\n </Suspense>\n )\n }\n\n // Add Engage widget if enabled (renders alongside, doesn't wrap)\n if (engage) {\n const engageConfig: EngageConfig =\n typeof engage === 'object' ? engage : {}\n content = (\n <>\n {content}\n <LazyEngageWidget\n apiUrl={apiUrl}\n apiKey={apiKey}\n projectId={projectId}\n position={engageConfig.position || 'bottom-right'}\n chatEnabled={engageConfig.chatEnabled !== false}\n />\n </>\n )\n }\n\n // Add SitemapSync if enabled\n if (sitemapSync) {\n content = (\n <>\n {content}\n <LazySitemapSync debug={debug} />\n </>\n )\n }\n\n return (\n <SiteKitIdentityProvider visitorId={visitorId} sessionId={sessionId}>\n {content}\n </SiteKitIdentityProvider>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/shared/identity.tsx","../src/layout/SiteKitClientProviders.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-DV2BURIN.mjs","sourcesContent":["/**\n * @sonordev/site-kit/shared — Unified Identity Management\n *\n * Single source of truth for visitor and session IDs across\n * Analytics, Signal, and Engage modules.\n *\n * Storage keys:\n * localStorage: _sk_vid (visitor ID, persists ~forever)\n * sessionStorage: _sk_sid (session ID, per-tab)\n * sessionStorage: _sk_stime (last activity timestamp for timeout)\n *\n * Storage keys are prefixed with `_sk_*`.\n */\n\n'use client'\n\nimport React, { createContext, useContext, useMemo } from 'react'\n\n// ============================================\n// ID Generation\n// ============================================\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID()\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)\n })\n}\n\n/**\n * Get or create a persistent visitor ID.\n * Stored in localStorage under `_sk_vid`.\n */\nexport function getOrCreateVisitorId(): string {\n if (typeof window === 'undefined') return ''\n\n let visitorId = localStorage.getItem('_sk_vid')\n\n if (!visitorId) {\n visitorId = generateId()\n }\n\n localStorage.setItem('_sk_vid', visitorId)\n return visitorId\n}\n\n/**\n * Get or create a session ID with inactivity timeout.\n * Stored in sessionStorage under `_sk_sid`.\n * Session expires after `timeoutMinutes` of inactivity (default 30).\n */\nexport function getOrCreateSessionId(timeoutMinutes = 30): string {\n if (typeof window === 'undefined') return ''\n\n const now = Date.now()\n const timeoutMs = timeoutMinutes * 60 * 1000\n\n const existingSession = sessionStorage.getItem('_sk_sid')\n const lastActivity = sessionStorage.getItem('_sk_stime')\n\n if (existingSession && lastActivity) {\n const elapsed = now - parseInt(lastActivity, 10)\n if (elapsed < timeoutMs) {\n // Session still active — refresh timestamp\n sessionStorage.setItem('_sk_stime', now.toString())\n sessionStorage.setItem('_sk_sid', existingSession)\n return existingSession\n }\n }\n\n // New session (first visit or timeout expired)\n const newSession = generateId()\n sessionStorage.setItem('_sk_sid', newSession)\n sessionStorage.setItem('_sk_stime', now.toString())\n return newSession\n}\n\n// ============================================\n// React Context\n// ============================================\n\nexport interface SiteKitIdentity {\n visitorId: string\n sessionId: string\n}\n\nconst SiteKitIdentityContext = createContext<SiteKitIdentity | null>(null)\n\nexport interface SiteKitIdentityProviderProps {\n visitorId: string\n sessionId: string\n children: React.ReactNode\n}\n\nexport function SiteKitIdentityProvider({\n visitorId,\n sessionId,\n children,\n}: SiteKitIdentityProviderProps) {\n const value = useMemo(() => ({ visitorId, sessionId }), [visitorId, sessionId])\n\n return (\n <SiteKitIdentityContext.Provider value={value}>\n {children}\n </SiteKitIdentityContext.Provider>\n )\n}\n\n/**\n * Access the shared visitor/session identity.\n * Falls back to generating IDs directly if used outside of SiteKitIdentityProvider.\n */\nexport function useSiteKitIdentity(): SiteKitIdentity {\n const context = useContext(SiteKitIdentityContext)\n if (context) return context\n\n // Fallback for standalone usage outside SiteKitLayout\n return {\n visitorId: getOrCreateVisitorId(),\n sessionId: getOrCreateSessionId(),\n }\n}\n","'use client'\n\n/**\n * SiteKitClientProviders — Client Island\n *\n * This is the 'use client' boundary for SiteKitLayout.\n * It composes the client-only providers (Analytics, Engage, Signal, SitemapSync)\n * that require React context or browser APIs.\n *\n * All modules are lazy-loaded via next/dynamic so their JS is only downloaded\n * when the feature is enabled — keeping the initial bundle lightweight.\n *\n * All modules share a unified identity (visitor ID + session ID) via\n * SiteKitIdentityProvider, ensuring consistent tracking across Analytics,\n * Signal, and Engage.\n *\n * Props are passed down from the server-side SiteKitLayout component,\n * which reads env vars at render time.\n */\n\nimport React, { Suspense, useCallback, useRef, useState, type ReactNode } from 'react'\nimport dynamic from 'next/dynamic'\nimport {\n SiteKitIdentityProvider,\n getOrCreateVisitorId,\n getOrCreateSessionId,\n} from '../shared/identity'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\n// Lazy-load feature modules — JS only downloaded when feature is enabled\nconst LazyAnalyticsProvider = dynamic(\n () => import('../analytics/AnalyticsProvider').then(m => ({ default: m.AnalyticsProvider })),\n { ssr: false },\n)\nconst LazyEngageWidget = dynamic(\n () => import('../engage/EngageWidget').then(m => ({ default: m.EngageWidget })),\n { ssr: false },\n)\nconst LazySignalBridge = dynamic(\n () => import('../signal/SignalBridge').then(m => ({ default: m.SignalBridge })),\n { ssr: false },\n)\nconst LazySitemapSync = dynamic(\n () => import('../SitemapSync').then(m => ({ default: m.SitemapSync })),\n { ssr: false },\n)\n\nexport interface SiteKitClientProvidersProps {\n children: ReactNode\n apiKey: string\n apiUrl: string\n projectId?: string\n analytics?: boolean | AnalyticsConfig\n engage?: boolean | EngageConfig\n signal?: boolean | SignalConfig\n sitemapSync?: boolean\n debug?: boolean\n}\n\nexport function SiteKitClientProviders({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n debug = false,\n}: SiteKitClientProvidersProps) {\n // Set window globals for modules that still read from them\n if (typeof window !== 'undefined') {\n ;(window as any).__SITE_KIT_API_URL__ = apiUrl\n ;(window as any).__SITE_KIT_API_KEY__ = apiKey\n ;(window as any).__SITE_KIT_DEBUG__ = debug\n }\n\n // Unified identity — single source of truth for all modules\n const [visitorId] = useState(() => getOrCreateVisitorId())\n const [sessionId] = useState(() => getOrCreateSessionId())\n\n // Page metadata bridge: Analytics writes, Signal reads\n const pageMetadataRef = useRef<Record<string, unknown> | null>(null)\n const onPageMetadata = useCallback((metadata: Record<string, unknown>) => {\n pageMetadataRef.current = metadata\n }, [])\n\n let content = <>{children}</>\n\n // Wrap with SignalBridge if enabled\n if (signal) {\n const signalConfig: SignalConfig =\n typeof signal === 'object' ? signal : {}\n content = (\n <LazySignalBridge\n enabled\n realtime={signalConfig.realtime !== false}\n experiments={signalConfig.experiments !== false}\n behaviorTracking={signalConfig.behaviorTracking !== false}\n visitorId={visitorId}\n sessionId={sessionId}\n pageMetadataRef={pageMetadataRef}\n >\n {content}\n </LazySignalBridge>\n )\n }\n\n // Wrap with Analytics if enabled\n if (analytics) {\n const analyticsConfig: AnalyticsConfig =\n typeof analytics === 'object' ? analytics : {}\n content = (\n <Suspense fallback={null}>\n <LazyAnalyticsProvider\n apiUrl={apiUrl}\n apiKey={apiKey}\n trackPageViews={analyticsConfig.trackPageViews !== false}\n trackWebVitals={analyticsConfig.trackWebVitals !== false}\n trackScrollDepth={analyticsConfig.trackScrollDepth !== false}\n trackClicks={analyticsConfig.trackClicks !== false}\n debug={debug}\n externalVisitorId={visitorId}\n externalSessionId={sessionId}\n onPageMetadata={onPageMetadata}\n >\n {content}\n </LazyAnalyticsProvider>\n </Suspense>\n )\n }\n\n // Add Engage widget if enabled (renders alongside, doesn't wrap)\n if (engage) {\n const engageConfig: EngageConfig =\n typeof engage === 'object' ? engage : {}\n content = (\n <>\n {content}\n <LazyEngageWidget\n apiUrl={apiUrl}\n apiKey={apiKey}\n projectId={projectId}\n position={engageConfig.position || 'bottom-right'}\n chatEnabled={engageConfig.chatEnabled !== false}\n />\n </>\n )\n }\n\n // Add SitemapSync if enabled\n if (sitemapSync) {\n content = (\n <>\n {content}\n <LazySitemapSync debug={debug} />\n </>\n )\n }\n\n return (\n <SiteKitIdentityProvider visitorId={visitorId} sessionId={sessionId}>\n {content}\n </SiteKitIdentityProvider>\n )\n}\n"]}
|
|
@@ -69,7 +69,7 @@ var LazySignalBridge = dynamic__default.default(
|
|
|
69
69
|
{ ssr: false }
|
|
70
70
|
);
|
|
71
71
|
var LazySitemapSync = dynamic__default.default(
|
|
72
|
-
() => import('./SitemapSync-
|
|
72
|
+
() => import('./SitemapSync-4Y65CMUK.js').then((m) => ({ default: m.SitemapSync })),
|
|
73
73
|
{ ssr: false }
|
|
74
74
|
);
|
|
75
75
|
function SiteKitClientProviders({
|
|
@@ -156,5 +156,5 @@ function SiteKitClientProviders({
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
exports.SiteKitClientProviders = SiteKitClientProviders;
|
|
159
|
-
//# sourceMappingURL=chunk-
|
|
160
|
-
//# sourceMappingURL=chunk-
|
|
159
|
+
//# sourceMappingURL=chunk-GVDPTXN3.js.map
|
|
160
|
+
//# sourceMappingURL=chunk-GVDPTXN3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/identity.tsx","../src/layout/SiteKitClientProviders.tsx"],"names":["createContext","useMemo","jsx","dynamic","useState","useRef","useCallback","Fragment","Suspense","jsxs"],"mappings":"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","file":"chunk-DPO3ZPFK.js","sourcesContent":["/**\n * @sonordev/site-kit/shared — Unified Identity Management\n *\n * Single source of truth for visitor and session IDs across\n * Analytics, Signal, and Engage modules.\n *\n * Storage keys:\n * localStorage: _sk_vid (visitor ID, persists ~forever)\n * sessionStorage: _sk_sid (session ID, per-tab)\n * sessionStorage: _sk_stime (last activity timestamp for timeout)\n *\n * Storage keys are prefixed with `_sk_*`.\n */\n\n'use client'\n\nimport React, { createContext, useContext, useMemo } from 'react'\n\n// ============================================\n// ID Generation\n// ============================================\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID()\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)\n })\n}\n\n/**\n * Get or create a persistent visitor ID.\n * Stored in localStorage under `_sk_vid`.\n */\nexport function getOrCreateVisitorId(): string {\n if (typeof window === 'undefined') return ''\n\n let visitorId = localStorage.getItem('_sk_vid')\n\n if (!visitorId) {\n visitorId = generateId()\n }\n\n localStorage.setItem('_sk_vid', visitorId)\n return visitorId\n}\n\n/**\n * Get or create a session ID with inactivity timeout.\n * Stored in sessionStorage under `_sk_sid`.\n * Session expires after `timeoutMinutes` of inactivity (default 30).\n */\nexport function getOrCreateSessionId(timeoutMinutes = 30): string {\n if (typeof window === 'undefined') return ''\n\n const now = Date.now()\n const timeoutMs = timeoutMinutes * 60 * 1000\n\n const existingSession = sessionStorage.getItem('_sk_sid')\n const lastActivity = sessionStorage.getItem('_sk_stime')\n\n if (existingSession && lastActivity) {\n const elapsed = now - parseInt(lastActivity, 10)\n if (elapsed < timeoutMs) {\n // Session still active — refresh timestamp\n sessionStorage.setItem('_sk_stime', now.toString())\n sessionStorage.setItem('_sk_sid', existingSession)\n return existingSession\n }\n }\n\n // New session (first visit or timeout expired)\n const newSession = generateId()\n sessionStorage.setItem('_sk_sid', newSession)\n sessionStorage.setItem('_sk_stime', now.toString())\n return newSession\n}\n\n// ============================================\n// React Context\n// ============================================\n\nexport interface SiteKitIdentity {\n visitorId: string\n sessionId: string\n}\n\nconst SiteKitIdentityContext = createContext<SiteKitIdentity | null>(null)\n\nexport interface SiteKitIdentityProviderProps {\n visitorId: string\n sessionId: string\n children: React.ReactNode\n}\n\nexport function SiteKitIdentityProvider({\n visitorId,\n sessionId,\n children,\n}: SiteKitIdentityProviderProps) {\n const value = useMemo(() => ({ visitorId, sessionId }), [visitorId, sessionId])\n\n return (\n <SiteKitIdentityContext.Provider value={value}>\n {children}\n </SiteKitIdentityContext.Provider>\n )\n}\n\n/**\n * Access the shared visitor/session identity.\n * Falls back to generating IDs directly if used outside of SiteKitIdentityProvider.\n */\nexport function useSiteKitIdentity(): SiteKitIdentity {\n const context = useContext(SiteKitIdentityContext)\n if (context) return context\n\n // Fallback for standalone usage outside SiteKitLayout\n return {\n visitorId: getOrCreateVisitorId(),\n sessionId: getOrCreateSessionId(),\n }\n}\n","'use client'\n\n/**\n * SiteKitClientProviders — Client Island\n *\n * This is the 'use client' boundary for SiteKitLayout.\n * It composes the client-only providers (Analytics, Engage, Signal, SitemapSync)\n * that require React context or browser APIs.\n *\n * All modules are lazy-loaded via next/dynamic so their JS is only downloaded\n * when the feature is enabled — keeping the initial bundle lightweight.\n *\n * All modules share a unified identity (visitor ID + session ID) via\n * SiteKitIdentityProvider, ensuring consistent tracking across Analytics,\n * Signal, and Engage.\n *\n * Props are passed down from the server-side SiteKitLayout component,\n * which reads env vars at render time.\n */\n\nimport React, { Suspense, useCallback, useRef, useState, type ReactNode } from 'react'\nimport dynamic from 'next/dynamic'\nimport {\n SiteKitIdentityProvider,\n getOrCreateVisitorId,\n getOrCreateSessionId,\n} from '../shared/identity'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\n// Lazy-load feature modules — JS only downloaded when feature is enabled\nconst LazyAnalyticsProvider = dynamic(\n () => import('../analytics/AnalyticsProvider').then(m => ({ default: m.AnalyticsProvider })),\n { ssr: false },\n)\nconst LazyEngageWidget = dynamic(\n () => import('../engage/EngageWidget').then(m => ({ default: m.EngageWidget })),\n { ssr: false },\n)\nconst LazySignalBridge = dynamic(\n () => import('../signal/SignalBridge').then(m => ({ default: m.SignalBridge })),\n { ssr: false },\n)\nconst LazySitemapSync = dynamic(\n () => import('../SitemapSync').then(m => ({ default: m.SitemapSync })),\n { ssr: false },\n)\n\nexport interface SiteKitClientProvidersProps {\n children: ReactNode\n apiKey: string\n apiUrl: string\n projectId?: string\n analytics?: boolean | AnalyticsConfig\n engage?: boolean | EngageConfig\n signal?: boolean | SignalConfig\n sitemapSync?: boolean\n debug?: boolean\n}\n\nexport function SiteKitClientProviders({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n debug = false,\n}: SiteKitClientProvidersProps) {\n // Set window globals for modules that still read from them\n if (typeof window !== 'undefined') {\n ;(window as any).__SITE_KIT_API_URL__ = apiUrl\n ;(window as any).__SITE_KIT_API_KEY__ = apiKey\n ;(window as any).__SITE_KIT_DEBUG__ = debug\n }\n\n // Unified identity — single source of truth for all modules\n const [visitorId] = useState(() => getOrCreateVisitorId())\n const [sessionId] = useState(() => getOrCreateSessionId())\n\n // Page metadata bridge: Analytics writes, Signal reads\n const pageMetadataRef = useRef<Record<string, unknown> | null>(null)\n const onPageMetadata = useCallback((metadata: Record<string, unknown>) => {\n pageMetadataRef.current = metadata\n }, [])\n\n let content = <>{children}</>\n\n // Wrap with SignalBridge if enabled\n if (signal) {\n const signalConfig: SignalConfig =\n typeof signal === 'object' ? signal : {}\n content = (\n <LazySignalBridge\n enabled\n realtime={signalConfig.realtime !== false}\n experiments={signalConfig.experiments !== false}\n behaviorTracking={signalConfig.behaviorTracking !== false}\n visitorId={visitorId}\n sessionId={sessionId}\n pageMetadataRef={pageMetadataRef}\n >\n {content}\n </LazySignalBridge>\n )\n }\n\n // Wrap with Analytics if enabled\n if (analytics) {\n const analyticsConfig: AnalyticsConfig =\n typeof analytics === 'object' ? analytics : {}\n content = (\n <Suspense fallback={null}>\n <LazyAnalyticsProvider\n apiUrl={apiUrl}\n apiKey={apiKey}\n trackPageViews={analyticsConfig.trackPageViews !== false}\n trackWebVitals={analyticsConfig.trackWebVitals !== false}\n trackScrollDepth={analyticsConfig.trackScrollDepth !== false}\n trackClicks={analyticsConfig.trackClicks !== false}\n debug={debug}\n externalVisitorId={visitorId}\n externalSessionId={sessionId}\n onPageMetadata={onPageMetadata}\n >\n {content}\n </LazyAnalyticsProvider>\n </Suspense>\n )\n }\n\n // Add Engage widget if enabled (renders alongside, doesn't wrap)\n if (engage) {\n const engageConfig: EngageConfig =\n typeof engage === 'object' ? engage : {}\n content = (\n <>\n {content}\n <LazyEngageWidget\n apiUrl={apiUrl}\n apiKey={apiKey}\n projectId={projectId}\n position={engageConfig.position || 'bottom-right'}\n chatEnabled={engageConfig.chatEnabled !== false}\n />\n </>\n )\n }\n\n // Add SitemapSync if enabled\n if (sitemapSync) {\n content = (\n <>\n {content}\n <LazySitemapSync debug={debug} />\n </>\n )\n }\n\n return (\n <SiteKitIdentityProvider visitorId={visitorId} sessionId={sessionId}>\n {content}\n </SiteKitIdentityProvider>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/shared/identity.tsx","../src/layout/SiteKitClientProviders.tsx"],"names":["createContext","useMemo","jsx","dynamic","useState","useRef","useCallback","Fragment","Suspense","jsxs"],"mappings":"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","file":"chunk-GVDPTXN3.js","sourcesContent":["/**\n * @sonordev/site-kit/shared — Unified Identity Management\n *\n * Single source of truth for visitor and session IDs across\n * Analytics, Signal, and Engage modules.\n *\n * Storage keys:\n * localStorage: _sk_vid (visitor ID, persists ~forever)\n * sessionStorage: _sk_sid (session ID, per-tab)\n * sessionStorage: _sk_stime (last activity timestamp for timeout)\n *\n * Storage keys are prefixed with `_sk_*`.\n */\n\n'use client'\n\nimport React, { createContext, useContext, useMemo } from 'react'\n\n// ============================================\n// ID Generation\n// ============================================\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID()\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)\n })\n}\n\n/**\n * Get or create a persistent visitor ID.\n * Stored in localStorage under `_sk_vid`.\n */\nexport function getOrCreateVisitorId(): string {\n if (typeof window === 'undefined') return ''\n\n let visitorId = localStorage.getItem('_sk_vid')\n\n if (!visitorId) {\n visitorId = generateId()\n }\n\n localStorage.setItem('_sk_vid', visitorId)\n return visitorId\n}\n\n/**\n * Get or create a session ID with inactivity timeout.\n * Stored in sessionStorage under `_sk_sid`.\n * Session expires after `timeoutMinutes` of inactivity (default 30).\n */\nexport function getOrCreateSessionId(timeoutMinutes = 30): string {\n if (typeof window === 'undefined') return ''\n\n const now = Date.now()\n const timeoutMs = timeoutMinutes * 60 * 1000\n\n const existingSession = sessionStorage.getItem('_sk_sid')\n const lastActivity = sessionStorage.getItem('_sk_stime')\n\n if (existingSession && lastActivity) {\n const elapsed = now - parseInt(lastActivity, 10)\n if (elapsed < timeoutMs) {\n // Session still active — refresh timestamp\n sessionStorage.setItem('_sk_stime', now.toString())\n sessionStorage.setItem('_sk_sid', existingSession)\n return existingSession\n }\n }\n\n // New session (first visit or timeout expired)\n const newSession = generateId()\n sessionStorage.setItem('_sk_sid', newSession)\n sessionStorage.setItem('_sk_stime', now.toString())\n return newSession\n}\n\n// ============================================\n// React Context\n// ============================================\n\nexport interface SiteKitIdentity {\n visitorId: string\n sessionId: string\n}\n\nconst SiteKitIdentityContext = createContext<SiteKitIdentity | null>(null)\n\nexport interface SiteKitIdentityProviderProps {\n visitorId: string\n sessionId: string\n children: React.ReactNode\n}\n\nexport function SiteKitIdentityProvider({\n visitorId,\n sessionId,\n children,\n}: SiteKitIdentityProviderProps) {\n const value = useMemo(() => ({ visitorId, sessionId }), [visitorId, sessionId])\n\n return (\n <SiteKitIdentityContext.Provider value={value}>\n {children}\n </SiteKitIdentityContext.Provider>\n )\n}\n\n/**\n * Access the shared visitor/session identity.\n * Falls back to generating IDs directly if used outside of SiteKitIdentityProvider.\n */\nexport function useSiteKitIdentity(): SiteKitIdentity {\n const context = useContext(SiteKitIdentityContext)\n if (context) return context\n\n // Fallback for standalone usage outside SiteKitLayout\n return {\n visitorId: getOrCreateVisitorId(),\n sessionId: getOrCreateSessionId(),\n }\n}\n","'use client'\n\n/**\n * SiteKitClientProviders — Client Island\n *\n * This is the 'use client' boundary for SiteKitLayout.\n * It composes the client-only providers (Analytics, Engage, Signal, SitemapSync)\n * that require React context or browser APIs.\n *\n * All modules are lazy-loaded via next/dynamic so their JS is only downloaded\n * when the feature is enabled — keeping the initial bundle lightweight.\n *\n * All modules share a unified identity (visitor ID + session ID) via\n * SiteKitIdentityProvider, ensuring consistent tracking across Analytics,\n * Signal, and Engage.\n *\n * Props are passed down from the server-side SiteKitLayout component,\n * which reads env vars at render time.\n */\n\nimport React, { Suspense, useCallback, useRef, useState, type ReactNode } from 'react'\nimport dynamic from 'next/dynamic'\nimport {\n SiteKitIdentityProvider,\n getOrCreateVisitorId,\n getOrCreateSessionId,\n} from '../shared/identity'\nimport type { AnalyticsConfig, EngageConfig, SignalConfig } from './types'\n\n// Lazy-load feature modules — JS only downloaded when feature is enabled\nconst LazyAnalyticsProvider = dynamic(\n () => import('../analytics/AnalyticsProvider').then(m => ({ default: m.AnalyticsProvider })),\n { ssr: false },\n)\nconst LazyEngageWidget = dynamic(\n () => import('../engage/EngageWidget').then(m => ({ default: m.EngageWidget })),\n { ssr: false },\n)\nconst LazySignalBridge = dynamic(\n () => import('../signal/SignalBridge').then(m => ({ default: m.SignalBridge })),\n { ssr: false },\n)\nconst LazySitemapSync = dynamic(\n () => import('../SitemapSync').then(m => ({ default: m.SitemapSync })),\n { ssr: false },\n)\n\nexport interface SiteKitClientProvidersProps {\n children: ReactNode\n apiKey: string\n apiUrl: string\n projectId?: string\n analytics?: boolean | AnalyticsConfig\n engage?: boolean | EngageConfig\n signal?: boolean | SignalConfig\n sitemapSync?: boolean\n debug?: boolean\n}\n\nexport function SiteKitClientProviders({\n children,\n apiKey,\n apiUrl,\n projectId,\n analytics = true,\n engage = true,\n signal = false,\n sitemapSync = true,\n debug = false,\n}: SiteKitClientProvidersProps) {\n // Set window globals for modules that still read from them\n if (typeof window !== 'undefined') {\n ;(window as any).__SITE_KIT_API_URL__ = apiUrl\n ;(window as any).__SITE_KIT_API_KEY__ = apiKey\n ;(window as any).__SITE_KIT_DEBUG__ = debug\n }\n\n // Unified identity — single source of truth for all modules\n const [visitorId] = useState(() => getOrCreateVisitorId())\n const [sessionId] = useState(() => getOrCreateSessionId())\n\n // Page metadata bridge: Analytics writes, Signal reads\n const pageMetadataRef = useRef<Record<string, unknown> | null>(null)\n const onPageMetadata = useCallback((metadata: Record<string, unknown>) => {\n pageMetadataRef.current = metadata\n }, [])\n\n let content = <>{children}</>\n\n // Wrap with SignalBridge if enabled\n if (signal) {\n const signalConfig: SignalConfig =\n typeof signal === 'object' ? signal : {}\n content = (\n <LazySignalBridge\n enabled\n realtime={signalConfig.realtime !== false}\n experiments={signalConfig.experiments !== false}\n behaviorTracking={signalConfig.behaviorTracking !== false}\n visitorId={visitorId}\n sessionId={sessionId}\n pageMetadataRef={pageMetadataRef}\n >\n {content}\n </LazySignalBridge>\n )\n }\n\n // Wrap with Analytics if enabled\n if (analytics) {\n const analyticsConfig: AnalyticsConfig =\n typeof analytics === 'object' ? analytics : {}\n content = (\n <Suspense fallback={null}>\n <LazyAnalyticsProvider\n apiUrl={apiUrl}\n apiKey={apiKey}\n trackPageViews={analyticsConfig.trackPageViews !== false}\n trackWebVitals={analyticsConfig.trackWebVitals !== false}\n trackScrollDepth={analyticsConfig.trackScrollDepth !== false}\n trackClicks={analyticsConfig.trackClicks !== false}\n debug={debug}\n externalVisitorId={visitorId}\n externalSessionId={sessionId}\n onPageMetadata={onPageMetadata}\n >\n {content}\n </LazyAnalyticsProvider>\n </Suspense>\n )\n }\n\n // Add Engage widget if enabled (renders alongside, doesn't wrap)\n if (engage) {\n const engageConfig: EngageConfig =\n typeof engage === 'object' ? engage : {}\n content = (\n <>\n {content}\n <LazyEngageWidget\n apiUrl={apiUrl}\n apiKey={apiKey}\n projectId={projectId}\n position={engageConfig.position || 'bottom-right'}\n chatEnabled={engageConfig.chatEnabled !== false}\n />\n </>\n )\n }\n\n // Add SitemapSync if enabled\n if (sitemapSync) {\n content = (\n <>\n {content}\n <LazySitemapSync debug={debug} />\n </>\n )\n }\n\n return (\n <SiteKitIdentityProvider visitorId={visitorId} sessionId={sessionId}>\n {content}\n </SiteKitIdentityProvider>\n )\n}\n"]}
|
|
@@ -39,7 +39,7 @@ async function parseSitemap(sitemapUrl, debug) {
|
|
|
39
39
|
const urls = doc.querySelectorAll("url");
|
|
40
40
|
const entries = [];
|
|
41
41
|
urls.forEach((url) => {
|
|
42
|
-
const loc = url.querySelector("loc")?.textContent;
|
|
42
|
+
const loc = url.querySelector("loc")?.textContent?.trim();
|
|
43
43
|
if (!loc) return;
|
|
44
44
|
let path;
|
|
45
45
|
try {
|
|
@@ -51,6 +51,10 @@ async function parseSitemap(sitemapUrl, debug) {
|
|
|
51
51
|
if (path !== "/" && path.endsWith("/")) {
|
|
52
52
|
path = path.slice(0, -1);
|
|
53
53
|
}
|
|
54
|
+
if (path.includes("://") || /\/\/[a-z0-9.-]+\.[a-z]{2,}/i.test(path)) {
|
|
55
|
+
if (debug) console.warn(`[SitemapSync] Skipping malformed path: ${path}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
54
58
|
const lastmod = url.querySelector("lastmod")?.textContent || void 0;
|
|
55
59
|
const changefreq = url.querySelector("changefreq")?.textContent || void 0;
|
|
56
60
|
const priorityStr = url.querySelector("priority")?.textContent;
|
|
@@ -149,5 +153,5 @@ function SitemapSync({
|
|
|
149
153
|
}
|
|
150
154
|
|
|
151
155
|
exports.SitemapSync = SitemapSync;
|
|
152
|
-
//# sourceMappingURL=chunk-
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
156
|
+
//# sourceMappingURL=chunk-JNXQX2R6.js.map
|
|
157
|
+
//# sourceMappingURL=chunk-JNXQX2R6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/SitemapSync.tsx"],"names":["useRef","useEffect"],"mappings":"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","file":"chunk-JNXQX2R6.js","sourcesContent":["/**\n * SitemapSync - Automatically sync sitemap.xml to Portal API\n *\n * Client-only component. Lives at package root so the main entry does not\n * pull in seo/ (and server-only code). For use in SiteKitProvider and\n * from @sonordev/site-kit/seo.\n *\n * @example\n * ```tsx\n * import { SitemapSync } from '@sonordev/site-kit'\n * // or\n * import { SitemapSync } from '@sonordev/site-kit/seo'\n * ```\n */\n\n'use client'\n\nimport { useEffect, useRef } from 'react'\n\ninterface SitemapSyncProps {\n /** Custom sitemap URL (defaults to /sitemap.xml) */\n sitemapUrl?: string\n /** How often to re-sync in minutes (0 = only on mount, default: 60) */\n syncInterval?: number\n /** Enable debug logging */\n debug?: boolean\n /**\n * Sync mode:\n * - 'additive' (default for runtime): adds/updates pages but never deletes existing ones\n * - 'full-replace': replaces the entire page set (use only for build-time sync via createSitemap)\n */\n mode?: 'additive' | 'full-replace'\n}\n\ninterface SitemapEntry {\n path: string\n lastmod?: string\n changefreq?: string\n priority?: number\n pageType?: string\n}\n\n/**\n * Infer page type from URL path pattern for Signal context.\n */\nfunction inferPageType(urlPath: string): string {\n if (urlPath === '/' || urlPath === '') return 'homepage'\n if (/^\\/services(\\/|$)/i.test(urlPath)) return 'service'\n if (/^\\/compare(\\/|$)/i.test(urlPath)) return 'comparison'\n if (/^\\/(insights|blog)(\\/|$)/i.test(urlPath)) return 'blog'\n if (/^\\/industries(\\/|$)/i.test(urlPath)) return 'industry'\n if (/^\\/work(\\/|$)/i.test(urlPath)) return 'portfolio'\n if (/^\\/faq(\\/|$)/i.test(urlPath)) return 'faq'\n if (/^\\/pricing(\\/|$)/i.test(urlPath)) return 'pricing'\n return 'page'\n}\n\nfunction getApiConfig() {\n if (typeof window === 'undefined') return { apiUrl: '', apiKey: '' }\n\n const apiUrl = (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n const apiKey = (window as any).__SITE_KIT_API_KEY__ || ''\n return { apiUrl, apiKey }\n}\n\nasync function parseSitemap(sitemapUrl: string, debug: boolean): Promise<SitemapEntry[]> {\n try {\n const response = await fetch(sitemapUrl)\n if (!response.ok) {\n if (debug) console.warn('[SitemapSync] Failed to fetch sitemap:', response.status)\n return []\n }\n\n const text = await response.text()\n const parser = new DOMParser()\n const doc = parser.parseFromString(text, 'application/xml')\n\n const parseError = doc.querySelector('parsererror')\n if (parseError) {\n if (debug) console.warn('[SitemapSync] Sitemap parse error:', parseError.textContent)\n return []\n }\n\n const urls = doc.querySelectorAll('url')\n const entries: SitemapEntry[] = []\n\n urls.forEach(url => {\n const loc = url.querySelector('loc')?.textContent?.trim()\n if (!loc) return\n\n let path: string\n try {\n const urlObj = new URL(loc)\n path = urlObj.pathname\n } catch {\n path = loc.startsWith('/') ? loc : `/${loc}`\n }\n\n if (path !== '/' && path.endsWith('/')) {\n path = path.slice(0, -1)\n }\n\n // Reject malformed paths (embedded domains, double protocols, etc.)\n if (path.includes('://') || /\\/\\/[a-z0-9.-]+\\.[a-z]{2,}/i.test(path)) {\n if (debug) console.warn(`[SitemapSync] Skipping malformed path: ${path}`)\n return\n }\n\n const lastmod = url.querySelector('lastmod')?.textContent || undefined\n const changefreq = url.querySelector('changefreq')?.textContent || undefined\n const priorityStr = url.querySelector('priority')?.textContent\n const priority = priorityStr ? parseFloat(priorityStr) : undefined\n\n entries.push({ path, lastmod, changefreq, priority })\n })\n\n if (debug) {\n console.log(`[SitemapSync] Parsed ${entries.length} entries from sitemap`)\n }\n\n return entries\n } catch (error) {\n if (debug) console.error('[SitemapSync] Error parsing sitemap:', error)\n return []\n }\n}\n\nasync function syncToPortal(\n entries: SitemapEntry[],\n apiUrl: string,\n apiKey: string,\n debug: boolean,\n mode: 'additive' | 'full-replace' = 'additive'\n): Promise<{ success: boolean; created: number; updated: number }> {\n if (!apiKey) {\n if (debug) console.warn('[SitemapSync] No API key configured')\n return { success: false, created: 0, updated: 0 }\n }\n\n if (entries.length === 0) {\n if (debug) console.log('[SitemapSync] No entries to sync')\n return { success: true, created: 0, updated: 0 }\n }\n\n try {\n const response = await fetch(`${apiUrl}/api/public/seo/register-sitemap`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify({\n entries: entries.map(e => ({\n path: e.path,\n priority: e.priority,\n changefreq: e.changefreq,\n pageType: inferPageType(e.path),\n })),\n mode,\n }),\n })\n\n if (!response.ok) {\n if (debug) console.error('[SitemapSync] API error:', response.status, response.statusText)\n return { success: false, created: 0, updated: 0 }\n }\n\n const result = await response.json()\n\n if (debug) {\n console.log(`[SitemapSync] Synced: ${result.created} created, ${result.updated} updated`)\n }\n\n return {\n success: true,\n created: result.created || 0,\n updated: result.updated || 0,\n }\n } catch (error) {\n if (debug) console.error('[SitemapSync] Sync error:', error)\n return { success: false, created: 0, updated: 0 }\n }\n}\n\nexport function SitemapSync({\n sitemapUrl = '/sitemap.xml',\n syncInterval = 60,\n debug = false,\n mode = 'additive',\n}: SitemapSyncProps) {\n const hasSyncedRef = useRef(false)\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\n\n useEffect(() => {\n // Build-time sync via createSitemap is the canonical source of truth, but\n // it can fail during ISR revalidation (app/ dir unavailable on serverless).\n // Runtime SitemapSync in additive mode is a safe fallback — it adds/updates\n // pages that may be missing without ever deleting existing ones.\n // Full-replace is never safe at runtime — always force additive.\n const effectiveMode: 'additive' | 'full-replace' = 'additive'\n\n const doSync = async () => {\n const { apiUrl, apiKey } = getApiConfig()\n\n if (!apiKey) {\n if (debug) console.warn('[SitemapSync] No API key found')\n return\n }\n\n if (debug) console.log('[SitemapSync] Starting sitemap sync...')\n\n const entries = await parseSitemap(sitemapUrl, debug)\n\n if (entries.length > 0) {\n await syncToPortal(entries, apiUrl, apiKey, debug, effectiveMode)\n }\n }\n\n if (!hasSyncedRef.current) {\n hasSyncedRef.current = true\n doSync()\n }\n\n if (syncInterval > 0) {\n intervalRef.current = setInterval(doSync, syncInterval * 60 * 1000)\n }\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current)\n }\n }\n }, [sitemapUrl, syncInterval, debug, mode])\n\n return null\n}\n"]}
|
|
@@ -37,7 +37,7 @@ async function parseSitemap(sitemapUrl, debug) {
|
|
|
37
37
|
const urls = doc.querySelectorAll("url");
|
|
38
38
|
const entries = [];
|
|
39
39
|
urls.forEach((url) => {
|
|
40
|
-
const loc = url.querySelector("loc")?.textContent;
|
|
40
|
+
const loc = url.querySelector("loc")?.textContent?.trim();
|
|
41
41
|
if (!loc) return;
|
|
42
42
|
let path;
|
|
43
43
|
try {
|
|
@@ -49,6 +49,10 @@ async function parseSitemap(sitemapUrl, debug) {
|
|
|
49
49
|
if (path !== "/" && path.endsWith("/")) {
|
|
50
50
|
path = path.slice(0, -1);
|
|
51
51
|
}
|
|
52
|
+
if (path.includes("://") || /\/\/[a-z0-9.-]+\.[a-z]{2,}/i.test(path)) {
|
|
53
|
+
if (debug) console.warn(`[SitemapSync] Skipping malformed path: ${path}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
52
56
|
const lastmod = url.querySelector("lastmod")?.textContent || void 0;
|
|
53
57
|
const changefreq = url.querySelector("changefreq")?.textContent || void 0;
|
|
54
58
|
const priorityStr = url.querySelector("priority")?.textContent;
|
|
@@ -147,5 +151,5 @@ function SitemapSync({
|
|
|
147
151
|
}
|
|
148
152
|
|
|
149
153
|
export { SitemapSync };
|
|
150
|
-
//# sourceMappingURL=chunk-
|
|
151
|
-
//# sourceMappingURL=chunk-
|
|
154
|
+
//# sourceMappingURL=chunk-ZUCVEQZB.mjs.map
|
|
155
|
+
//# sourceMappingURL=chunk-ZUCVEQZB.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/SitemapSync.tsx"],"names":[],"mappings":"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","file":"chunk-ZUCVEQZB.mjs","sourcesContent":["/**\n * SitemapSync - Automatically sync sitemap.xml to Portal API\n *\n * Client-only component. Lives at package root so the main entry does not\n * pull in seo/ (and server-only code). For use in SiteKitProvider and\n * from @sonordev/site-kit/seo.\n *\n * @example\n * ```tsx\n * import { SitemapSync } from '@sonordev/site-kit'\n * // or\n * import { SitemapSync } from '@sonordev/site-kit/seo'\n * ```\n */\n\n'use client'\n\nimport { useEffect, useRef } from 'react'\n\ninterface SitemapSyncProps {\n /** Custom sitemap URL (defaults to /sitemap.xml) */\n sitemapUrl?: string\n /** How often to re-sync in minutes (0 = only on mount, default: 60) */\n syncInterval?: number\n /** Enable debug logging */\n debug?: boolean\n /**\n * Sync mode:\n * - 'additive' (default for runtime): adds/updates pages but never deletes existing ones\n * - 'full-replace': replaces the entire page set (use only for build-time sync via createSitemap)\n */\n mode?: 'additive' | 'full-replace'\n}\n\ninterface SitemapEntry {\n path: string\n lastmod?: string\n changefreq?: string\n priority?: number\n pageType?: string\n}\n\n/**\n * Infer page type from URL path pattern for Signal context.\n */\nfunction inferPageType(urlPath: string): string {\n if (urlPath === '/' || urlPath === '') return 'homepage'\n if (/^\\/services(\\/|$)/i.test(urlPath)) return 'service'\n if (/^\\/compare(\\/|$)/i.test(urlPath)) return 'comparison'\n if (/^\\/(insights|blog)(\\/|$)/i.test(urlPath)) return 'blog'\n if (/^\\/industries(\\/|$)/i.test(urlPath)) return 'industry'\n if (/^\\/work(\\/|$)/i.test(urlPath)) return 'portfolio'\n if (/^\\/faq(\\/|$)/i.test(urlPath)) return 'faq'\n if (/^\\/pricing(\\/|$)/i.test(urlPath)) return 'pricing'\n return 'page'\n}\n\nfunction getApiConfig() {\n if (typeof window === 'undefined') return { apiUrl: '', apiKey: '' }\n\n const apiUrl = (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n const apiKey = (window as any).__SITE_KIT_API_KEY__ || ''\n return { apiUrl, apiKey }\n}\n\nasync function parseSitemap(sitemapUrl: string, debug: boolean): Promise<SitemapEntry[]> {\n try {\n const response = await fetch(sitemapUrl)\n if (!response.ok) {\n if (debug) console.warn('[SitemapSync] Failed to fetch sitemap:', response.status)\n return []\n }\n\n const text = await response.text()\n const parser = new DOMParser()\n const doc = parser.parseFromString(text, 'application/xml')\n\n const parseError = doc.querySelector('parsererror')\n if (parseError) {\n if (debug) console.warn('[SitemapSync] Sitemap parse error:', parseError.textContent)\n return []\n }\n\n const urls = doc.querySelectorAll('url')\n const entries: SitemapEntry[] = []\n\n urls.forEach(url => {\n const loc = url.querySelector('loc')?.textContent?.trim()\n if (!loc) return\n\n let path: string\n try {\n const urlObj = new URL(loc)\n path = urlObj.pathname\n } catch {\n path = loc.startsWith('/') ? loc : `/${loc}`\n }\n\n if (path !== '/' && path.endsWith('/')) {\n path = path.slice(0, -1)\n }\n\n // Reject malformed paths (embedded domains, double protocols, etc.)\n if (path.includes('://') || /\\/\\/[a-z0-9.-]+\\.[a-z]{2,}/i.test(path)) {\n if (debug) console.warn(`[SitemapSync] Skipping malformed path: ${path}`)\n return\n }\n\n const lastmod = url.querySelector('lastmod')?.textContent || undefined\n const changefreq = url.querySelector('changefreq')?.textContent || undefined\n const priorityStr = url.querySelector('priority')?.textContent\n const priority = priorityStr ? parseFloat(priorityStr) : undefined\n\n entries.push({ path, lastmod, changefreq, priority })\n })\n\n if (debug) {\n console.log(`[SitemapSync] Parsed ${entries.length} entries from sitemap`)\n }\n\n return entries\n } catch (error) {\n if (debug) console.error('[SitemapSync] Error parsing sitemap:', error)\n return []\n }\n}\n\nasync function syncToPortal(\n entries: SitemapEntry[],\n apiUrl: string,\n apiKey: string,\n debug: boolean,\n mode: 'additive' | 'full-replace' = 'additive'\n): Promise<{ success: boolean; created: number; updated: number }> {\n if (!apiKey) {\n if (debug) console.warn('[SitemapSync] No API key configured')\n return { success: false, created: 0, updated: 0 }\n }\n\n if (entries.length === 0) {\n if (debug) console.log('[SitemapSync] No entries to sync')\n return { success: true, created: 0, updated: 0 }\n }\n\n try {\n const response = await fetch(`${apiUrl}/api/public/seo/register-sitemap`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify({\n entries: entries.map(e => ({\n path: e.path,\n priority: e.priority,\n changefreq: e.changefreq,\n pageType: inferPageType(e.path),\n })),\n mode,\n }),\n })\n\n if (!response.ok) {\n if (debug) console.error('[SitemapSync] API error:', response.status, response.statusText)\n return { success: false, created: 0, updated: 0 }\n }\n\n const result = await response.json()\n\n if (debug) {\n console.log(`[SitemapSync] Synced: ${result.created} created, ${result.updated} updated`)\n }\n\n return {\n success: true,\n created: result.created || 0,\n updated: result.updated || 0,\n }\n } catch (error) {\n if (debug) console.error('[SitemapSync] Sync error:', error)\n return { success: false, created: 0, updated: 0 }\n }\n}\n\nexport function SitemapSync({\n sitemapUrl = '/sitemap.xml',\n syncInterval = 60,\n debug = false,\n mode = 'additive',\n}: SitemapSyncProps) {\n const hasSyncedRef = useRef(false)\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\n\n useEffect(() => {\n // Build-time sync via createSitemap is the canonical source of truth, but\n // it can fail during ISR revalidation (app/ dir unavailable on serverless).\n // Runtime SitemapSync in additive mode is a safe fallback — it adds/updates\n // pages that may be missing without ever deleting existing ones.\n // Full-replace is never safe at runtime — always force additive.\n const effectiveMode: 'additive' | 'full-replace' = 'additive'\n\n const doSync = async () => {\n const { apiUrl, apiKey } = getApiConfig()\n\n if (!apiKey) {\n if (debug) console.warn('[SitemapSync] No API key found')\n return\n }\n\n if (debug) console.log('[SitemapSync] Starting sitemap sync...')\n\n const entries = await parseSitemap(sitemapUrl, debug)\n\n if (entries.length > 0) {\n await syncToPortal(entries, apiUrl, apiKey, debug, effectiveMode)\n }\n }\n\n if (!hasSyncedRef.current) {\n hasSyncedRef.current = true\n doSync()\n }\n\n if (syncInterval > 0) {\n intervalRef.current = setInterval(doSync, syncInterval * 60 * 1000)\n }\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current)\n }\n }\n }, [sitemapUrl, syncInterval, debug, mode])\n\n return null\n}\n"]}
|
package/dist/layout/client.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var chunkGVDPTXN3_js = require('../chunk-GVDPTXN3.js');
|
|
5
5
|
require('../chunk-ZSMWDLMK.js');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
Object.defineProperty(exports, "SiteKitClientProviders", {
|
|
10
10
|
enumerable: true,
|
|
11
|
-
get: function () { return
|
|
11
|
+
get: function () { return chunkGVDPTXN3_js.SiteKitClientProviders; }
|
|
12
12
|
});
|
|
13
13
|
//# sourceMappingURL=client.js.map
|
|
14
14
|
//# sourceMappingURL=client.js.map
|
package/dist/layout/client.mjs
CHANGED
package/dist/layout/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkGVDPTXN3_js = require('../chunk-GVDPTXN3.js');
|
|
4
4
|
var chunkGCPPXUAI_js = require('../chunk-GCPPXUAI.js');
|
|
5
5
|
var chunkPKUTYMN7_js = require('../chunk-PKUTYMN7.js');
|
|
6
6
|
require('../chunk-VU4GL5ZG.js');
|
|
@@ -41,7 +41,7 @@ async function SiteKitLayout({
|
|
|
41
41
|
favicon && /* @__PURE__ */ jsxRuntime.jsx(chunkGCPPXUAI_js.ManagedFavicon, { apiKey: resolvedApiKey, apiUrl: resolvedApiUrl }),
|
|
42
42
|
managedScripts && /* @__PURE__ */ jsxRuntime.jsx(chunkPKUTYMN7_js.ManagedScripts, { position: "head" }),
|
|
43
43
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44
|
-
|
|
44
|
+
chunkGVDPTXN3_js.SiteKitClientProviders,
|
|
45
45
|
{
|
|
46
46
|
apiKey: resolvedApiKey,
|
|
47
47
|
apiUrl: resolvedApiUrl,
|
package/dist/layout/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SiteKitClientProviders } from '../chunk-
|
|
1
|
+
import { SiteKitClientProviders } from '../chunk-DV2BURIN.mjs';
|
|
2
2
|
import { ManagedFavicon } from '../chunk-CU6OGBKK.mjs';
|
|
3
3
|
import { ManagedScripts } from '../chunk-L7XPPFSS.mjs';
|
|
4
4
|
import '../chunk-TUFP3FGI.mjs';
|
package/dist/seo/client.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var chunkJNXQX2R6_js = require('../chunk-JNXQX2R6.js');
|
|
5
5
|
require('../chunk-ZSMWDLMK.js');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
Object.defineProperty(exports, "SitemapSync", {
|
|
10
10
|
enumerable: true,
|
|
11
|
-
get: function () { return
|
|
11
|
+
get: function () { return chunkJNXQX2R6_js.SitemapSync; }
|
|
12
12
|
});
|
|
13
13
|
//# sourceMappingURL=client.js.map
|
|
14
14
|
//# sourceMappingURL=client.js.map
|
package/dist/seo/client.mjs
CHANGED
package/dist/seo/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunkPKUTYMN7_js = require('../chunk-PKUTYMN7.js');
|
|
4
|
-
var
|
|
4
|
+
var chunkJNXQX2R6_js = require('../chunk-JNXQX2R6.js');
|
|
5
5
|
var chunkAWMEH65F_js = require('../chunk-AWMEH65F.js');
|
|
6
6
|
var chunkVU4GL5ZG_js = require('../chunk-VU4GL5ZG.js');
|
|
7
7
|
require('../chunk-ZSMWDLMK.js');
|
|
@@ -649,7 +649,7 @@ Object.defineProperty(exports, "ManagedScripts", {
|
|
|
649
649
|
});
|
|
650
650
|
Object.defineProperty(exports, "SitemapSync", {
|
|
651
651
|
enumerable: true,
|
|
652
|
-
get: function () { return
|
|
652
|
+
get: function () { return chunkJNXQX2R6_js.SitemapSync; }
|
|
653
653
|
});
|
|
654
654
|
Object.defineProperty(exports, "generateSitemap", {
|
|
655
655
|
enumerable: true,
|
package/dist/seo/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ManagedNoScripts, ManagedScripts } from '../chunk-L7XPPFSS.mjs';
|
|
2
|
-
export { SitemapSync } from '../chunk-
|
|
2
|
+
export { SitemapSync } from '../chunk-ZUCVEQZB.mjs';
|
|
3
3
|
import { getManagedMetadata } from '../chunk-DTVZJPVM.mjs';
|
|
4
4
|
export { generateSitemap, getABVariant, getManagedMetadata, getManagedMetadataWithAB, getRedirect, getRobotsDirective, isIndexable } from '../chunk-DTVZJPVM.mjs';
|
|
5
5
|
import { getContentBlock, getSchemaMarkups, getSEOPageData, getEntityEnhancedSchema, getFAQData, getInternalLinks, getEntities } from '../chunk-TUFP3FGI.mjs';
|
package/dist/sitemap/index.js
CHANGED
|
@@ -75,11 +75,7 @@ async function tryGenerateStaticParams(pageFilePath) {
|
|
|
75
75
|
return params;
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
} catch
|
|
79
|
-
console.warn(
|
|
80
|
-
`[site-kit] Could not resolve generateStaticParams from ${pageFilePath}:`,
|
|
81
|
-
err.message
|
|
82
|
-
);
|
|
78
|
+
} catch {
|
|
83
79
|
}
|
|
84
80
|
return null;
|
|
85
81
|
}
|
|
@@ -150,8 +146,8 @@ async function discoverPages(appDir, currentPath = "", pages = [], options = {})
|
|
|
150
146
|
}
|
|
151
147
|
}
|
|
152
148
|
} else {
|
|
153
|
-
console.
|
|
154
|
-
`[site-kit]
|
|
149
|
+
console.debug(
|
|
150
|
+
`[site-kit] Dynamic segment "${entry}" at /${currentPath} not resolved via filesystem. If these routes are missing from sitemap, add them via additionalPaths or dynamicRoutes config.`
|
|
155
151
|
);
|
|
156
152
|
}
|
|
157
153
|
continue;
|
|
@@ -262,6 +258,7 @@ async function syncSitemapToPortal(entries, apiUrl, apiKey, options) {
|
|
|
262
258
|
} catch {
|
|
263
259
|
path = entry.url.startsWith("/") ? entry.url : `/${entry.url}`;
|
|
264
260
|
}
|
|
261
|
+
path = path.replace(/^\/{1,2}[a-z0-9.-]+\.[a-z]{2,}(\/|$)/i, "/");
|
|
265
262
|
const normalized = {
|
|
266
263
|
path,
|
|
267
264
|
priority: entry.priority,
|
|
@@ -272,6 +269,13 @@ async function syncSitemapToPortal(entries, apiUrl, apiKey, options) {
|
|
|
272
269
|
normalized.description = entry.description;
|
|
273
270
|
}
|
|
274
271
|
return normalized;
|
|
272
|
+
}).filter((entry) => {
|
|
273
|
+
const p = entry.path;
|
|
274
|
+
if (p.includes("://") || /\/\/[a-z0-9.-]+\.[a-z]{2,}/i.test(p)) {
|
|
275
|
+
console.warn(`[site-kit] Filtering malformed sitemap path: ${p}`);
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
return true;
|
|
275
279
|
});
|
|
276
280
|
try {
|
|
277
281
|
const body = { entries: normalizedEntries, mode: "full-replace" };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/sitemap/index.ts"],"names":["pathToFileURL","join","existsSync","readdirSync","statSync","resolve","getSiteConfig","writeLLMsTxtToPublic"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @sonordev/site-kit - Sitemap Generator\n * \n * Automatically generates sitemap.xml from Next.js app directory structure.\n * Works at build time to discover all pages and sync them to Portal API.\n * \n * @example\n * ```ts\n * // app/sitemap.ts\n * import { createSitemap } from '@sonordev/site-kit/sitemap'\n * \n * export default createSitemap({\n * baseUrl: 'https://example.com',\n * // Optional: exclude patterns\n * exclude: ['/admin/*', '/api/*'],\n * // Optional: additional dynamic routes\n * additionalPaths: async () => [\n * { path: '/blog/post-1', priority: 0.7 },\n * ]\n * })\n * ```\n */\n\nimport { readdirSync, statSync, existsSync } from 'fs'\nimport { join, resolve } from 'path'\nimport { pathToFileURL } from 'url'\nimport { writeLLMsTxtToPublic } from '../llms/writeLLMsTxt'\nimport { getSiteConfig } from '../site-config'\n\nexport type PageType = 'homepage' | 'service' | 'comparison' | 'blog' | 'industry' | 'portfolio' | 'faq' | 'pricing' | 'page'\n\nexport interface SitemapEntry {\n url: string\n lastModified?: Date | string\n changeFrequency?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'\n priority?: number\n /** Optional page description for Signal context */\n description?: string\n /** Inferred or explicit page type for Signal context */\n pageType?: PageType\n}\n\nexport interface SitemapConfig {\n /** Base URL for the site (optional when apiKey is set; then resolved from Portal project-info) */\n baseUrl?: string\n /** Glob patterns to exclude (e.g., ['/admin/*', '/api/*']) */\n exclude?: string[]\n /** Default priority for pages */\n defaultPriority?: number\n /** Default change frequency */\n defaultChangeFrequency?: SitemapEntry['changeFrequency']\n /** Additional paths to include (for dynamic routes) */\n additionalPaths?: () => Promise<Array<{ path: string; priority?: number; changeFrequency?: SitemapEntry['changeFrequency']; description?: string }>>\n /**\n * Explicit values for dynamic route segments.\n * Keys are the segment names (e.g. '[slug]') and values are arrays of param values.\n * This is a simpler alternative to relying on generateStaticParams auto-discovery.\n * @example { '[slug]': ['seo', 'analytics', 'crm', 'echo'] }\n */\n dynamicRoutes?: Record<string, string[]>\n /** When true (default), attempt to import generateStaticParams from dynamic route pages at build time */\n resolveGenerateStaticParams?: boolean\n /** Priority overrides by path pattern */\n priorities?: Record<string, number>\n /** Portal API URL for syncing (defaults to SONOR_API_URL or https://api.sonor.io) */\n apiUrl?: string\n /** Portal API key for syncing (defaults to SONOR_API_KEY) */\n apiKey?: string\n /** Disable auto-sync to Portal API */\n disableSync?: boolean\n /** When true, await meta optimization before build completes (for build-time llms.txt with managed_llm_schema) */\n awaitMetaOptimization?: boolean\n /** When true (default when API key present), write AI-optimized llms.txt to public/llms.txt after sitemap sync */\n optimizedLLMsTxt?: boolean\n /** When true with optimizedLLMsTxt, also write public/llms-full.txt */\n optimizedLLMsFullTxt?: boolean\n /** When Portal returns empty services, use this to supply local site data for llms.txt */\n getLocalData?: () => Promise<import('../llms/types').LLMsDataResponse | null>\n /** Use Signal AI visibility scores to compute page priority (40% visibility + 30% depth + 30% base) */\n intelligentPriority?: boolean\n}\n\n// Route groups and special folders to ignore\nconst IGNORED_FOLDERS = [\n 'api',\n 'admin',\n '_uptrade',\n '%5Fuptrade', // URL-encoded _uptrade\n 'sonor-setup',\n 'offline',\n]\n\n// File patterns that indicate a page\nconst PAGE_FILES = ['page.tsx', 'page.jsx', 'page.js', 'page.ts']\n\n/**\n * Strip trailing slash from a path (preserving '/' root).\n * Used to normalize paths before comparison so that '/about' and '/about/' are treated the same.\n */\nfunction normalizePath(path: string): string {\n if (path !== '/' && path.endsWith('/')) {\n return path.slice(0, -1)\n }\n return path\n}\n\n/**\n * Check if a path matches any exclude pattern.\n * Normalizes both the path and pattern prefixes to handle trailing slash inconsistencies.\n */\nfunction isExcluded(path: string, patterns: string[]): boolean {\n const normalized = normalizePath(path)\n return patterns.some(pattern => {\n if (pattern.endsWith('/*')) {\n const prefix = normalizePath(pattern.slice(0, -2))\n return normalized === prefix || normalized.startsWith(prefix + '/')\n }\n if (pattern.endsWith('*')) {\n const prefix = pattern.slice(0, -1)\n return normalized.startsWith(prefix)\n }\n return normalized === normalizePath(pattern)\n })\n}\n\n/**\n * Infer page type from URL path pattern.\n * Used to give Signal context about what each page covers.\n */\nfunction inferPageType(urlPath: string): PageType {\n if (urlPath === '/') return 'homepage'\n if (/^\\/services(\\/|$)/i.test(urlPath)) return 'service'\n if (/^\\/compare(\\/|$)/i.test(urlPath)) return 'comparison'\n if (/^\\/(insights|blog)(\\/|$)/i.test(urlPath)) return 'blog'\n if (/^\\/industries(\\/|$)/i.test(urlPath)) return 'industry'\n if (/^\\/work(\\/|$)/i.test(urlPath)) return 'portfolio'\n if (/^\\/faq(\\/|$)/i.test(urlPath)) return 'faq'\n if (/^\\/pricing(\\/|$)/i.test(urlPath)) return 'pricing'\n return 'page'\n}\n\n/**\n * Convert a file-system path (relative to appDir) into a URL path,\n * stripping route groups and cleaning slashes.\n */\nfunction toUrlPath(fsPath: string): string {\n let urlPath = '/' + fsPath\n // Handle route groups (parentheses) - remove them from URL\n urlPath = urlPath.replace(/\\/\\([^)]+\\)/g, '')\n // Clean up path\n urlPath = urlPath.replace(/\\/+/g, '/') // Remove double slashes\n if (urlPath !== '/' && urlPath.endsWith('/')) {\n urlPath = urlPath.slice(0, -1) // Remove trailing slash\n }\n return urlPath\n}\n\n/**\n * Attempt to call generateStaticParams from a dynamic route page module.\n * Returns the param objects, or null if unavailable.\n */\nasync function tryGenerateStaticParams(\n pageFilePath: string\n): Promise<Array<Record<string, string>> | null> {\n try {\n const fileUrl = pathToFileURL(pageFilePath).href\n // Use a timeout to guard against slow external API calls\n const importPromise = import(fileUrl)\n const timeoutPromise = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('generateStaticParams import timed out (5s)')), 5000)\n )\n const mod = await Promise.race([importPromise, timeoutPromise])\n\n if (typeof mod.generateStaticParams === 'function') {\n const resultPromise = Promise.resolve(mod.generateStaticParams())\n const resultTimeout = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('generateStaticParams call timed out (10s)')), 10000)\n )\n const params = await Promise.race([resultPromise, resultTimeout])\n if (Array.isArray(params)) {\n return params as Array<Record<string, string>>\n }\n }\n } catch (err) {\n console.warn(\n `[site-kit] Could not resolve generateStaticParams from ${pageFilePath}:`,\n (err as Error).message\n )\n }\n return null\n}\n\ninterface DiscoverPagesOptions {\n /** Explicit param values for dynamic segments, keyed by segment name e.g. '[slug]' */\n dynamicRoutes?: Record<string, string[]>\n /** Whether to attempt auto-import of generateStaticParams (default true) */\n resolveGenerateStaticParams?: boolean\n}\n\n/**\n * Recursively discover pages from the app directory.\n * Handles dynamic route segments via explicit config or generateStaticParams auto-discovery.\n */\nasync function discoverPages(\n appDir: string,\n currentPath: string = '',\n pages: string[] = [],\n options: DiscoverPagesOptions = {}\n): Promise<string[]> {\n const fullPath = join(appDir, currentPath)\n\n if (!existsSync(fullPath)) {\n return pages\n }\n\n const entries = readdirSync(fullPath)\n\n // Check if this directory has a page file\n const hasPage = entries.some(entry => PAGE_FILES.includes(entry))\n\n if (hasPage) {\n const urlPath = toUrlPath(currentPath)\n // Only add if the path has no unresolved dynamic segments\n if (!urlPath.includes('[')) {\n pages.push(urlPath)\n }\n }\n\n // Recurse into subdirectories\n for (const entry of entries) {\n const entryPath = join(fullPath, entry)\n const stat = statSync(entryPath)\n\n if (stat.isDirectory()) {\n // Skip ignored folders\n const folderName = entry.toLowerCase()\n if (IGNORED_FOLDERS.some(ignored => folderName.includes(ignored))) {\n continue\n }\n\n // Skip private folders\n if (entry.startsWith('_')) {\n continue\n }\n\n // Handle dynamic route segments\n if (entry.startsWith('[') && entry.endsWith(']')) {\n const paramName = entry.slice(1, -1) // e.g. 'slug' from '[slug]'\n let paramValues: string[] | null = null\n\n // 1. Check explicit dynamicRoutes config first\n if (options.dynamicRoutes) {\n // Match by full segment name '[slug]' or bare param name 'slug'\n paramValues = options.dynamicRoutes[entry] ?? options.dynamicRoutes[paramName] ?? null\n }\n\n // 2. Fall back to auto-importing generateStaticParams\n if (!paramValues && options.resolveGenerateStaticParams !== false) {\n // Find the page file inside the dynamic directory\n const dynamicDirPath = join(fullPath, entry)\n const pageFile = PAGE_FILES.find(pf => existsSync(join(dynamicDirPath, pf)))\n\n if (pageFile) {\n const pageFilePath = resolve(dynamicDirPath, pageFile)\n const params = await tryGenerateStaticParams(pageFilePath)\n if (params && params.length > 0) {\n // Extract the values for this specific param\n paramValues = params\n .map(p => p[paramName])\n .filter((v): v is string => typeof v === 'string')\n }\n }\n }\n\n if (paramValues && paramValues.length > 0) {\n const dynamicDirPath = join(fullPath, entry)\n const dynamicEntries = readdirSync(dynamicDirPath)\n\n // Check if the dynamic directory itself has a page file\n const dynamicHasPage = dynamicEntries.some(e => PAGE_FILES.includes(e))\n\n for (const value of paramValues) {\n // If the dynamic dir has a page, add the resolved path\n if (dynamicHasPage) {\n const resolvedPath = toUrlPath(join(currentPath, value))\n if (!resolvedPath.includes('[')) {\n pages.push(resolvedPath)\n }\n }\n\n // Recurse into subdirectories inside the dynamic segment,\n // mapping [param] on disk to the resolved value in the URL\n for (const subEntry of dynamicEntries) {\n if (PAGE_FILES.includes(subEntry)) continue // skip page files\n const subEntryPath = join(dynamicDirPath, subEntry)\n if (statSync(subEntryPath).isDirectory()) {\n // For nested dynamic segments or regular subdirs, recurse\n // We need a custom appDir-relative path that uses the resolved value\n // but reads from the dynamic dir on disk\n await discoverPagesFromDisk(\n dynamicDirPath,\n subEntry,\n join(currentPath, value),\n pages,\n options\n )\n }\n }\n }\n } else {\n console.warn(\n `[site-kit] Skipping dynamic segment \"${entry}\" at /${currentPath} — no params resolved. ` +\n `Provide dynamicRoutes config or export generateStaticParams from the page.`\n )\n }\n continue\n }\n\n await discoverPages(appDir, join(currentPath, entry), pages, options)\n }\n }\n\n return pages\n}\n\n/**\n * Discover pages starting from a disk path that differs from the URL path.\n * Used when traversing inside dynamic route segments where the filesystem\n * directory name (e.g. [slug]) differs from the URL segment (e.g. 'seo').\n */\nasync function discoverPagesFromDisk(\n diskParent: string,\n diskEntry: string,\n urlParentPath: string,\n pages: string[],\n options: DiscoverPagesOptions\n): Promise<void> {\n const diskPath = join(diskParent, diskEntry)\n if (!existsSync(diskPath) || !statSync(diskPath).isDirectory()) return\n\n const entries = readdirSync(diskPath)\n const hasPage = entries.some(e => PAGE_FILES.includes(e))\n const urlPath = toUrlPath(join(urlParentPath, diskEntry))\n\n if (hasPage && !urlPath.includes('[')) {\n pages.push(urlPath)\n }\n\n for (const sub of entries) {\n if (PAGE_FILES.includes(sub)) continue\n const subPath = join(diskPath, sub)\n if (!statSync(subPath).isDirectory()) continue\n\n const folderName = sub.toLowerCase()\n if (IGNORED_FOLDERS.some(ignored => folderName.includes(ignored))) continue\n if (sub.startsWith('_')) continue\n\n if (sub.startsWith('[') && sub.endsWith(']')) {\n // Nested dynamic segment — resolve its params too\n const nestedParam = sub.slice(1, -1)\n let nestedValues: string[] | null = null\n\n if (options.dynamicRoutes) {\n nestedValues = options.dynamicRoutes[sub] ?? options.dynamicRoutes[nestedParam] ?? null\n }\n\n if (!nestedValues && options.resolveGenerateStaticParams !== false) {\n const nestedDir = join(diskPath, sub)\n const pageFile = PAGE_FILES.find(pf => existsSync(join(nestedDir, pf)))\n if (pageFile) {\n const params = await tryGenerateStaticParams(resolve(nestedDir, pageFile))\n if (params && params.length > 0) {\n nestedValues = params.map(p => p[nestedParam]).filter((v): v is string => typeof v === 'string')\n }\n }\n }\n\n if (nestedValues && nestedValues.length > 0) {\n const nestedDir = join(diskPath, sub)\n const nestedEntries = readdirSync(nestedDir)\n const nestedHasPage = nestedEntries.some(e => PAGE_FILES.includes(e))\n for (const val of nestedValues) {\n if (nestedHasPage) {\n const resolved = toUrlPath(join(urlParentPath, diskEntry, val))\n if (!resolved.includes('[')) pages.push(resolved)\n }\n for (const nestedSub of nestedEntries) {\n if (PAGE_FILES.includes(nestedSub)) continue\n const nestedSubPath = join(nestedDir, nestedSub)\n if (statSync(nestedSubPath).isDirectory()) {\n await discoverPagesFromDisk(nestedDir, nestedSub, join(urlParentPath, diskEntry, val), pages, options)\n }\n }\n }\n }\n continue\n }\n\n await discoverPagesFromDisk(diskPath, sub, join(urlParentPath, diskEntry), pages, options)\n }\n}\n\n/**\n * Find the app directory from the current working directory\n */\nfunction findAppDir(): string {\n const cwd = process.cwd()\n \n // Try common locations\n const candidates = [\n join(cwd, 'app'),\n join(cwd, 'src', 'app'),\n ]\n \n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate\n }\n }\n \n throw new Error(\n '[site-kit] Could not find app directory. Ensure you have an \"app\" or \"src/app\" folder.'\n )\n}\n\n/**\n * Get priority for a path based on config\n */\nfunction getPriority(path: string, config: SitemapConfig): number {\n const normalized = normalizePath(path)\n // Check priority overrides\n if (config.priorities) {\n for (const [pattern, priority] of Object.entries(config.priorities)) {\n if (pattern.endsWith('/*')) {\n const prefix = normalizePath(pattern.slice(0, -2))\n if (normalized === prefix || normalized.startsWith(prefix + '/')) return priority\n } else if (normalized === normalizePath(pattern)) {\n return priority\n }\n }\n }\n \n // Home page gets highest priority\n if (path === '/') return 1.0\n \n // Shallow pages get higher priority\n const depth = path.split('/').filter(Boolean).length\n if (depth === 1) return 0.8\n if (depth === 2) return 0.6\n \n return config.defaultPriority ?? 0.5\n}\n\nexport interface SyncSitemapOptions {\n /** Trigger Signal AI meta optimization after sync */\n optimizeMeta?: boolean\n /** When true, wait for meta optimization to complete (for build-time llms.txt) */\n awaitMeta?: boolean\n}\n\n/**\n * Sync sitemap entries to Portal API\n * Called automatically by createSitemap at build time\n */\nexport async function syncSitemapToPortal(\n entries: SitemapEntry[],\n apiUrl: string,\n apiKey: string,\n options?: SyncSitemapOptions\n): Promise<{ success: boolean; created: number; updated: number }> {\n if (!apiKey) {\n console.warn('[site-kit] No API key provided, skipping sitemap sync')\n return { success: false, created: 0, updated: 0 }\n }\n \n const normalizedEntries = entries.map(entry => {\n let path: string\n try {\n const url = new URL(entry.url)\n path = url.pathname\n } catch {\n path = entry.url.startsWith('/') ? entry.url : `/${entry.url}`\n }\n\n const normalized: Record<string, unknown> = {\n path,\n priority: entry.priority,\n changefreq: entry.changeFrequency,\n pageType: entry.pageType ?? inferPageType(path),\n }\n if (entry.description) {\n normalized.description = entry.description\n }\n return normalized\n })\n \n try {\n const body: Record<string, unknown> = { entries: normalizedEntries, mode: 'full-replace' }\n if (options?.optimizeMeta !== undefined) body.optimize_meta = options.optimizeMeta\n if (options?.awaitMeta !== undefined) body.await_meta = options.awaitMeta\n\n const response = await fetch(`${apiUrl}/api/public/seo/register-sitemap`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify(body),\n })\n \n if (!response.ok) {\n console.error('[site-kit] Sitemap sync failed:', response.status, await response.text())\n return { success: false, created: 0, updated: 0 }\n }\n \n const result = await response.json()\n \n return {\n success: true,\n created: result.created || 0,\n updated: result.updated || 0,\n }\n } catch (error) {\n console.error('[site-kit] Sitemap sync error:', error)\n return { success: false, created: 0, updated: 0 }\n }\n}\n\n/**\n * Create a sitemap generator function for Next.js\n * Automatically syncs to Portal API at build time.\n * \n * @example\n * ```ts\n * // app/sitemap.ts\n * import { createSitemap } from '@sonordev/site-kit/sitemap'\n * \n * export default createSitemap({\n * baseUrl: 'https://example.com'\n * })\n * ```\n */\n/**\n * Fetches published portfolio items from Portal API and returns them as sitemap paths.\n * Use this in createSitemap's additionalPaths to include portfolio routes.\n *\n * @example\n * ```ts\n * import { createSitemap, getPortfolioPaths } from '@sonordev/site-kit/sitemap'\n *\n * export default createSitemap({\n * baseUrl: 'https://example.com',\n * additionalPaths: async () => [\n * ...(await getPortfolioPaths()),\n * ]\n * })\n * ```\n */\nexport async function getPortfolioPaths(options?: {\n apiUrl?: string\n apiKey?: string\n basePath?: string // defaults to '/portfolio'\n priority?: number // defaults to 0.7\n}): Promise<Array<{ path: string; priority: number; changeFrequency: 'weekly' }>> {\n const apiUrl = options?.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n const apiKey = options?.apiKey || process.env.SONOR_API_KEY || process.env.UPTRADE_API_KEY || ''\n const basePath = options?.basePath || '/work'\n const priority = options?.priority || 0.7\n\n if (!apiKey) return []\n\n try {\n const response = await fetch(`${apiUrl}/api/public/portfolio/items`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n })\n\n if (!response.ok) return []\n\n const data = await response.json()\n const items = data?.portfolioItems || data?.items || data || []\n\n return items\n .filter((item: any) => item.slug && item.status === 'published')\n .map((item: any) => ({\n path: `${basePath}/${item.slug}`,\n priority,\n changeFrequency: 'weekly' as const,\n }))\n } catch {\n console.warn('[site-kit] Failed to fetch portfolio items for sitemap')\n return []\n }\n}\n\nexport function createSitemap(config: SitemapConfig): () => SitemapEntry[] | Promise<SitemapEntry[]> {\n return async () => {\n const { exclude = [], defaultChangeFrequency = 'weekly' } = config\n \n // Resolve baseUrl: from config, or from Portal via apiKey/env\n let baseUrl = config.baseUrl\n if (!baseUrl) {\n const apiKey = config.apiKey ?? process.env.SONOR_API_KEY\n const site = await getSiteConfig({ apiKey, apiUrl: config.apiUrl })\n baseUrl = site?.site_url ?? process.env.NEXT_PUBLIC_SITE_URL ?? 'https://example.com'\n }\n const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl\n \n // Default exclusions\n const allExclusions = [\n '/api/*',\n '/admin/*',\n '/_uptrade/*',\n '/sonor-setup/*',\n '/offline/*',\n ...exclude,\n ]\n \n // Discover pages from app directory (including dynamic routes)\n let pages: string[] = []\n let discoveryFailed = false\n try {\n const appDir = findAppDir()\n pages = await discoverPages(appDir, '', [], {\n dynamicRoutes: config.dynamicRoutes,\n resolveGenerateStaticParams: config.resolveGenerateStaticParams,\n })\n } catch (error) {\n console.warn('[site-kit] Failed to discover pages from filesystem:', (error as Error).message)\n // Filesystem discovery failed (typical during ISR revalidation on Netlify/Vercel\n // where the app/ directory doesn't exist in the standalone serverless bundle).\n // Fetch the page list from Portal API instead — it has the correct data from\n // the last build-time full-replace sync.\n discoveryFailed = true\n const apiKey = config.apiKey || process.env.SONOR_API_KEY || process.env.UPTRADE_API_KEY || ''\n const apiUrl = config.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n if (apiKey && normalizedBaseUrl) {\n try {\n const domain = normalizedBaseUrl.replace(/^https?:\\/\\/(www\\.)?/, '')\n const pagesResponse = await fetch(\n `${apiUrl}/api/public/seo/metadata?domain=${encodeURIComponent(domain)}`,\n { headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey } }\n )\n if (pagesResponse.ok) {\n const pagesData = await pagesResponse.json()\n const pagesList: Array<{ path: string }> = pagesData?.pagesList || []\n pages = pagesList.map(p => p.path).filter(Boolean)\n console.log(`[site-kit] Recovered ${pages.length} pages from Portal API (ISR fallback)`)\n }\n } catch (fetchErr) {\n console.warn('[site-kit] Portal API fallback failed:', (fetchErr as Error).message)\n }\n }\n if (pages.length === 0) {\n pages = ['/']\n }\n }\n \n // Normalize, deduplicate, and filter out excluded pages\n pages = Array.from(new Set(pages.map(normalizePath))).filter(page => !isExcluded(page, allExclusions))\n\n // Build sitemap entries\n const entries: SitemapEntry[] = pages.map(path => ({\n url: `${normalizedBaseUrl}${path}`,\n lastModified: new Date(),\n changeFrequency: defaultChangeFrequency,\n priority: getPriority(path, config),\n pageType: inferPageType(path),\n }))\n\n // Add additional dynamic paths if provided (deduplicating against discovered pages)\n if (config.additionalPaths) {\n try {\n // Normalize URLs for dedup so '/about' and '/about/' are treated the same\n const existingPaths = new Set(pages)\n const additional = await config.additionalPaths()\n for (const item of additional) {\n const normalizedItemPath = normalizePath(item.path)\n if (!isExcluded(normalizedItemPath, allExclusions) && !existingPaths.has(normalizedItemPath)) {\n entries.push({\n url: `${normalizedBaseUrl}${normalizedItemPath}`,\n lastModified: new Date(),\n changeFrequency: item.changeFrequency ?? defaultChangeFrequency,\n priority: item.priority ?? getPriority(normalizedItemPath, config),\n pageType: inferPageType(normalizedItemPath),\n ...(item.description ? { description: item.description } : {}),\n })\n existingPaths.add(normalizedItemPath)\n }\n }\n } catch (error) {\n console.warn('[site-kit] Failed to get additional paths:', error)\n }\n }\n \n // Intelligent priority: blend visibility scores with depth-based priority\n if (config.intelligentPriority) {\n try {\n const apiKey = config.apiKey ?? process.env.SONOR_API_KEY ?? ''\n const projectIdMatch = apiKey.match(/^(?:sonor|uptrade)_([0-9a-f-]{36})_/)\n const projectId = projectIdMatch ? projectIdMatch[1] : null\n\n if (projectId) {\n const signalApiUrl = process.env.SONOR_SIGNAL_URL || process.env.SIGNAL_API_URL || process.env.NEXT_PUBLIC_SIGNAL_API_URL || 'https://signal.sonor.io'\n const visResponse = await fetch(`${signalApiUrl}/skills/seo/visibility/${projectId}`, {\n headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },\n }).catch(() => null)\n\n if (visResponse?.ok) {\n const visData = await visResponse.json()\n const scores = (visData?.data || visData) as Array<{ page_path: string; overall_score: number }>\n const scoreMap = new Map(scores.map(s => [s.page_path, s.overall_score]))\n\n let appliedCount = 0\n for (const entry of entries) {\n const path = new URL(entry.url).pathname\n const visScore = scoreMap.get(path)\n if (visScore !== undefined) {\n const depthBonus = path === '/' ? 1.0 : Math.max(0.3, 1.0 - (path.split('/').filter(Boolean).length * 0.15))\n // 40% visibility + 30% depth + 30% base\n entry.priority = Math.round((visScore * 0.4 + depthBonus * 0.3 + (entry.priority ?? 0.5) * 0.3) * 100) / 100\n appliedCount++\n }\n }\n if (appliedCount > 0) {\n console.log(`[site-kit] Applied intelligent priority from ${appliedCount} visibility scores`)\n }\n }\n }\n } catch {\n // Signal API unavailable — fall back to static priority\n }\n }\n\n // Sort by priority descending\n entries.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))\n\n console.log(`[site-kit] Generated sitemap with ${entries.length} pages`)\n \n // Auto-sync to Portal API at build time only (skip in dev — sitemap.xml is re-rendered on every request)\n // Also skip sync when page discovery failed (e.g. ISR revalidation where the app/ directory\n // doesn't exist in the standalone serverless bundle). Running full-replace with only\n // additionalPaths would DELETE all properly-synced static routes discovered at build time.\n const isDev = process.env.NODE_ENV === 'development'\n if (!config.disableSync && !isDev && !discoveryFailed) {\n const apiUrl = config.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n const apiKey = config.apiKey || process.env.SONOR_API_KEY\n \n if (apiKey) {\n try {\n const syncOptions: SyncSitemapOptions = {}\n if (config.awaitMetaOptimization === true) {\n syncOptions.optimizeMeta = true\n syncOptions.awaitMeta = true\n }\n const result = await syncSitemapToPortal(entries, apiUrl, apiKey, syncOptions)\n if (result.success) {\n console.log(`[site-kit] Synced to Portal API: ${result.created} created, ${result.updated} updated`)\n\n // Verification: report meta optimization coverage after sync\n if (config.awaitMetaOptimization === true) {\n try {\n const domain = normalizedBaseUrl.replace(/^https?:\\/\\/(www\\.)?/, '')\n const pagesResponse = await fetch(\n `${apiUrl}/api/public/seo/metadata?domain=${encodeURIComponent(domain)}`,\n { headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey } }\n )\n if (pagesResponse.ok) {\n const pagesData = await pagesResponse.json()\n const allPages: Array<{ managed_title?: string; managed_meta_description?: string }> = pagesData?.pagesList || []\n const optimized = allPages.filter(p => p.managed_title && p.managed_meta_description)\n console.log(`[site-kit] Meta optimization coverage: ${optimized.length}/${allPages.length} pages (${allPages.length ? Math.round((optimized.length / allPages.length) * 100) : 0}%)`)\n }\n } catch {\n console.warn('[site-kit] Could not verify meta optimization coverage')\n }\n }\n }\n\n // Write AI-optimized llms.txt after sync (default true when API key present)\n if (config.optimizedLLMsTxt !== false) {\n try {\n const writeResult = await writeLLMsTxtToPublic({\n apiUrl,\n apiKey,\n full: config.optimizedLLMsFullTxt ?? false,\n fallbackToLocal: true,\n getLocalData: config.getLocalData,\n })\n if (writeResult.success) {\n console.log(`[site-kit] Wrote llms.txt (optimized=${writeResult.optimized})`)\n }\n } catch (err) {\n console.warn('[site-kit] Failed to write llms.txt:', (err as Error).message)\n }\n }\n } catch (err) {\n console.warn('[site-kit] Failed to sync sitemap to Portal API:', (err as Error).message)\n }\n } else {\n console.log('[site-kit] No API key found, skipping Portal API sync')\n }\n }\n \n return entries\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/sitemap/index.ts"],"names":["pathToFileURL","join","existsSync","readdirSync","statSync","resolve","getSiteConfig","writeLLMsTxtToPublic"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @sonordev/site-kit - Sitemap Generator\n * \n * Automatically generates sitemap.xml from Next.js app directory structure.\n * Works at build time to discover all pages and sync them to Portal API.\n * \n * @example\n * ```ts\n * // app/sitemap.ts\n * import { createSitemap } from '@sonordev/site-kit/sitemap'\n * \n * export default createSitemap({\n * baseUrl: 'https://example.com',\n * // Optional: exclude patterns\n * exclude: ['/admin/*', '/api/*'],\n * // Optional: additional dynamic routes\n * additionalPaths: async () => [\n * { path: '/blog/post-1', priority: 0.7 },\n * ]\n * })\n * ```\n */\n\nimport { readdirSync, statSync, existsSync } from 'fs'\nimport { join, resolve } from 'path'\nimport { pathToFileURL } from 'url'\nimport { writeLLMsTxtToPublic } from '../llms/writeLLMsTxt'\nimport { getSiteConfig } from '../site-config'\n\nexport type PageType = 'homepage' | 'service' | 'comparison' | 'blog' | 'industry' | 'portfolio' | 'faq' | 'pricing' | 'page'\n\nexport interface SitemapEntry {\n url: string\n lastModified?: Date | string\n changeFrequency?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'\n priority?: number\n /** Optional page description for Signal context */\n description?: string\n /** Inferred or explicit page type for Signal context */\n pageType?: PageType\n}\n\nexport interface SitemapConfig {\n /** Base URL for the site (optional when apiKey is set; then resolved from Portal project-info) */\n baseUrl?: string\n /** Glob patterns to exclude (e.g., ['/admin/*', '/api/*']) */\n exclude?: string[]\n /** Default priority for pages */\n defaultPriority?: number\n /** Default change frequency */\n defaultChangeFrequency?: SitemapEntry['changeFrequency']\n /** Additional paths to include (for dynamic routes) */\n additionalPaths?: () => Promise<Array<{ path: string; priority?: number; changeFrequency?: SitemapEntry['changeFrequency']; description?: string }>>\n /**\n * Explicit values for dynamic route segments.\n * Keys are the segment names (e.g. '[slug]') and values are arrays of param values.\n * This is a simpler alternative to relying on generateStaticParams auto-discovery.\n * @example { '[slug]': ['seo', 'analytics', 'crm', 'echo'] }\n */\n dynamicRoutes?: Record<string, string[]>\n /** When true (default), attempt to import generateStaticParams from dynamic route pages at build time */\n resolveGenerateStaticParams?: boolean\n /** Priority overrides by path pattern */\n priorities?: Record<string, number>\n /** Portal API URL for syncing (defaults to SONOR_API_URL or https://api.sonor.io) */\n apiUrl?: string\n /** Portal API key for syncing (defaults to SONOR_API_KEY) */\n apiKey?: string\n /** Disable auto-sync to Portal API */\n disableSync?: boolean\n /** When true, await meta optimization before build completes (for build-time llms.txt with managed_llm_schema) */\n awaitMetaOptimization?: boolean\n /** When true (default when API key present), write AI-optimized llms.txt to public/llms.txt after sitemap sync */\n optimizedLLMsTxt?: boolean\n /** When true with optimizedLLMsTxt, also write public/llms-full.txt */\n optimizedLLMsFullTxt?: boolean\n /** When Portal returns empty services, use this to supply local site data for llms.txt */\n getLocalData?: () => Promise<import('../llms/types').LLMsDataResponse | null>\n /** Use Signal AI visibility scores to compute page priority (40% visibility + 30% depth + 30% base) */\n intelligentPriority?: boolean\n}\n\n// Route groups and special folders to ignore\nconst IGNORED_FOLDERS = [\n 'api',\n 'admin',\n '_uptrade',\n '%5Fuptrade', // URL-encoded _uptrade\n 'sonor-setup',\n 'offline',\n]\n\n// File patterns that indicate a page\nconst PAGE_FILES = ['page.tsx', 'page.jsx', 'page.js', 'page.ts']\n\n/**\n * Strip trailing slash from a path (preserving '/' root).\n * Used to normalize paths before comparison so that '/about' and '/about/' are treated the same.\n */\nfunction normalizePath(path: string): string {\n if (path !== '/' && path.endsWith('/')) {\n return path.slice(0, -1)\n }\n return path\n}\n\n/**\n * Check if a path matches any exclude pattern.\n * Normalizes both the path and pattern prefixes to handle trailing slash inconsistencies.\n */\nfunction isExcluded(path: string, patterns: string[]): boolean {\n const normalized = normalizePath(path)\n return patterns.some(pattern => {\n if (pattern.endsWith('/*')) {\n const prefix = normalizePath(pattern.slice(0, -2))\n return normalized === prefix || normalized.startsWith(prefix + '/')\n }\n if (pattern.endsWith('*')) {\n const prefix = pattern.slice(0, -1)\n return normalized.startsWith(prefix)\n }\n return normalized === normalizePath(pattern)\n })\n}\n\n/**\n * Infer page type from URL path pattern.\n * Used to give Signal context about what each page covers.\n */\nfunction inferPageType(urlPath: string): PageType {\n if (urlPath === '/') return 'homepage'\n if (/^\\/services(\\/|$)/i.test(urlPath)) return 'service'\n if (/^\\/compare(\\/|$)/i.test(urlPath)) return 'comparison'\n if (/^\\/(insights|blog)(\\/|$)/i.test(urlPath)) return 'blog'\n if (/^\\/industries(\\/|$)/i.test(urlPath)) return 'industry'\n if (/^\\/work(\\/|$)/i.test(urlPath)) return 'portfolio'\n if (/^\\/faq(\\/|$)/i.test(urlPath)) return 'faq'\n if (/^\\/pricing(\\/|$)/i.test(urlPath)) return 'pricing'\n return 'page'\n}\n\n/**\n * Convert a file-system path (relative to appDir) into a URL path,\n * stripping route groups and cleaning slashes.\n */\nfunction toUrlPath(fsPath: string): string {\n let urlPath = '/' + fsPath\n // Handle route groups (parentheses) - remove them from URL\n urlPath = urlPath.replace(/\\/\\([^)]+\\)/g, '')\n // Clean up path\n urlPath = urlPath.replace(/\\/+/g, '/') // Remove double slashes\n if (urlPath !== '/' && urlPath.endsWith('/')) {\n urlPath = urlPath.slice(0, -1) // Remove trailing slash\n }\n return urlPath\n}\n\n/**\n * Attempt to call generateStaticParams from a dynamic route page module.\n * Returns the param objects, or null if unavailable.\n */\nasync function tryGenerateStaticParams(\n pageFilePath: string\n): Promise<Array<Record<string, string>> | null> {\n try {\n const fileUrl = pathToFileURL(pageFilePath).href\n // Use a timeout to guard against slow external API calls\n const importPromise = import(fileUrl)\n const timeoutPromise = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('generateStaticParams import timed out (5s)')), 5000)\n )\n const mod = await Promise.race([importPromise, timeoutPromise])\n\n if (typeof mod.generateStaticParams === 'function') {\n const resultPromise = Promise.resolve(mod.generateStaticParams())\n const resultTimeout = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('generateStaticParams call timed out (10s)')), 10000)\n )\n const params = await Promise.race([resultPromise, resultTimeout])\n if (Array.isArray(params)) {\n return params as Array<Record<string, string>>\n }\n }\n } catch {\n // Expected on Next.js 16+ where dynamic import() of page files fails\n // with \"Cannot find module as expression is too dynamic\".\n // This is harmless — dynamic routes are resolved via additionalPaths or dynamicRoutes config.\n }\n return null\n}\n\ninterface DiscoverPagesOptions {\n /** Explicit param values for dynamic segments, keyed by segment name e.g. '[slug]' */\n dynamicRoutes?: Record<string, string[]>\n /** Whether to attempt auto-import of generateStaticParams (default true) */\n resolveGenerateStaticParams?: boolean\n}\n\n/**\n * Recursively discover pages from the app directory.\n * Handles dynamic route segments via explicit config or generateStaticParams auto-discovery.\n */\nasync function discoverPages(\n appDir: string,\n currentPath: string = '',\n pages: string[] = [],\n options: DiscoverPagesOptions = {}\n): Promise<string[]> {\n const fullPath = join(appDir, currentPath)\n\n if (!existsSync(fullPath)) {\n return pages\n }\n\n const entries = readdirSync(fullPath)\n\n // Check if this directory has a page file\n const hasPage = entries.some(entry => PAGE_FILES.includes(entry))\n\n if (hasPage) {\n const urlPath = toUrlPath(currentPath)\n // Only add if the path has no unresolved dynamic segments\n if (!urlPath.includes('[')) {\n pages.push(urlPath)\n }\n }\n\n // Recurse into subdirectories\n for (const entry of entries) {\n const entryPath = join(fullPath, entry)\n const stat = statSync(entryPath)\n\n if (stat.isDirectory()) {\n // Skip ignored folders\n const folderName = entry.toLowerCase()\n if (IGNORED_FOLDERS.some(ignored => folderName.includes(ignored))) {\n continue\n }\n\n // Skip private folders\n if (entry.startsWith('_')) {\n continue\n }\n\n // Handle dynamic route segments\n if (entry.startsWith('[') && entry.endsWith(']')) {\n const paramName = entry.slice(1, -1) // e.g. 'slug' from '[slug]'\n let paramValues: string[] | null = null\n\n // 1. Check explicit dynamicRoutes config first\n if (options.dynamicRoutes) {\n // Match by full segment name '[slug]' or bare param name 'slug'\n paramValues = options.dynamicRoutes[entry] ?? options.dynamicRoutes[paramName] ?? null\n }\n\n // 2. Fall back to auto-importing generateStaticParams\n if (!paramValues && options.resolveGenerateStaticParams !== false) {\n // Find the page file inside the dynamic directory\n const dynamicDirPath = join(fullPath, entry)\n const pageFile = PAGE_FILES.find(pf => existsSync(join(dynamicDirPath, pf)))\n\n if (pageFile) {\n const pageFilePath = resolve(dynamicDirPath, pageFile)\n const params = await tryGenerateStaticParams(pageFilePath)\n if (params && params.length > 0) {\n // Extract the values for this specific param\n paramValues = params\n .map(p => p[paramName])\n .filter((v): v is string => typeof v === 'string')\n }\n }\n }\n\n if (paramValues && paramValues.length > 0) {\n const dynamicDirPath = join(fullPath, entry)\n const dynamicEntries = readdirSync(dynamicDirPath)\n\n // Check if the dynamic directory itself has a page file\n const dynamicHasPage = dynamicEntries.some(e => PAGE_FILES.includes(e))\n\n for (const value of paramValues) {\n // If the dynamic dir has a page, add the resolved path\n if (dynamicHasPage) {\n const resolvedPath = toUrlPath(join(currentPath, value))\n if (!resolvedPath.includes('[')) {\n pages.push(resolvedPath)\n }\n }\n\n // Recurse into subdirectories inside the dynamic segment,\n // mapping [param] on disk to the resolved value in the URL\n for (const subEntry of dynamicEntries) {\n if (PAGE_FILES.includes(subEntry)) continue // skip page files\n const subEntryPath = join(dynamicDirPath, subEntry)\n if (statSync(subEntryPath).isDirectory()) {\n // For nested dynamic segments or regular subdirs, recurse\n // We need a custom appDir-relative path that uses the resolved value\n // but reads from the dynamic dir on disk\n await discoverPagesFromDisk(\n dynamicDirPath,\n subEntry,\n join(currentPath, value),\n pages,\n options\n )\n }\n }\n }\n } else {\n // Only log at debug level — dynamic segments are typically handled\n // by additionalPaths (blog posts, portfolio items, etc.)\n console.debug(\n `[site-kit] Dynamic segment \"${entry}\" at /${currentPath} not resolved via filesystem. ` +\n `If these routes are missing from sitemap, add them via additionalPaths or dynamicRoutes config.`\n )\n }\n continue\n }\n\n await discoverPages(appDir, join(currentPath, entry), pages, options)\n }\n }\n\n return pages\n}\n\n/**\n * Discover pages starting from a disk path that differs from the URL path.\n * Used when traversing inside dynamic route segments where the filesystem\n * directory name (e.g. [slug]) differs from the URL segment (e.g. 'seo').\n */\nasync function discoverPagesFromDisk(\n diskParent: string,\n diskEntry: string,\n urlParentPath: string,\n pages: string[],\n options: DiscoverPagesOptions\n): Promise<void> {\n const diskPath = join(diskParent, diskEntry)\n if (!existsSync(diskPath) || !statSync(diskPath).isDirectory()) return\n\n const entries = readdirSync(diskPath)\n const hasPage = entries.some(e => PAGE_FILES.includes(e))\n const urlPath = toUrlPath(join(urlParentPath, diskEntry))\n\n if (hasPage && !urlPath.includes('[')) {\n pages.push(urlPath)\n }\n\n for (const sub of entries) {\n if (PAGE_FILES.includes(sub)) continue\n const subPath = join(diskPath, sub)\n if (!statSync(subPath).isDirectory()) continue\n\n const folderName = sub.toLowerCase()\n if (IGNORED_FOLDERS.some(ignored => folderName.includes(ignored))) continue\n if (sub.startsWith('_')) continue\n\n if (sub.startsWith('[') && sub.endsWith(']')) {\n // Nested dynamic segment — resolve its params too\n const nestedParam = sub.slice(1, -1)\n let nestedValues: string[] | null = null\n\n if (options.dynamicRoutes) {\n nestedValues = options.dynamicRoutes[sub] ?? options.dynamicRoutes[nestedParam] ?? null\n }\n\n if (!nestedValues && options.resolveGenerateStaticParams !== false) {\n const nestedDir = join(diskPath, sub)\n const pageFile = PAGE_FILES.find(pf => existsSync(join(nestedDir, pf)))\n if (pageFile) {\n const params = await tryGenerateStaticParams(resolve(nestedDir, pageFile))\n if (params && params.length > 0) {\n nestedValues = params.map(p => p[nestedParam]).filter((v): v is string => typeof v === 'string')\n }\n }\n }\n\n if (nestedValues && nestedValues.length > 0) {\n const nestedDir = join(diskPath, sub)\n const nestedEntries = readdirSync(nestedDir)\n const nestedHasPage = nestedEntries.some(e => PAGE_FILES.includes(e))\n for (const val of nestedValues) {\n if (nestedHasPage) {\n const resolved = toUrlPath(join(urlParentPath, diskEntry, val))\n if (!resolved.includes('[')) pages.push(resolved)\n }\n for (const nestedSub of nestedEntries) {\n if (PAGE_FILES.includes(nestedSub)) continue\n const nestedSubPath = join(nestedDir, nestedSub)\n if (statSync(nestedSubPath).isDirectory()) {\n await discoverPagesFromDisk(nestedDir, nestedSub, join(urlParentPath, diskEntry, val), pages, options)\n }\n }\n }\n }\n continue\n }\n\n await discoverPagesFromDisk(diskPath, sub, join(urlParentPath, diskEntry), pages, options)\n }\n}\n\n/**\n * Find the app directory from the current working directory\n */\nfunction findAppDir(): string {\n const cwd = process.cwd()\n \n // Try common locations\n const candidates = [\n join(cwd, 'app'),\n join(cwd, 'src', 'app'),\n ]\n \n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate\n }\n }\n \n throw new Error(\n '[site-kit] Could not find app directory. Ensure you have an \"app\" or \"src/app\" folder.'\n )\n}\n\n/**\n * Get priority for a path based on config\n */\nfunction getPriority(path: string, config: SitemapConfig): number {\n const normalized = normalizePath(path)\n // Check priority overrides\n if (config.priorities) {\n for (const [pattern, priority] of Object.entries(config.priorities)) {\n if (pattern.endsWith('/*')) {\n const prefix = normalizePath(pattern.slice(0, -2))\n if (normalized === prefix || normalized.startsWith(prefix + '/')) return priority\n } else if (normalized === normalizePath(pattern)) {\n return priority\n }\n }\n }\n \n // Home page gets highest priority\n if (path === '/') return 1.0\n \n // Shallow pages get higher priority\n const depth = path.split('/').filter(Boolean).length\n if (depth === 1) return 0.8\n if (depth === 2) return 0.6\n \n return config.defaultPriority ?? 0.5\n}\n\nexport interface SyncSitemapOptions {\n /** Trigger Signal AI meta optimization after sync */\n optimizeMeta?: boolean\n /** When true, wait for meta optimization to complete (for build-time llms.txt) */\n awaitMeta?: boolean\n}\n\n/**\n * Sync sitemap entries to Portal API\n * Called automatically by createSitemap at build time\n */\nexport async function syncSitemapToPortal(\n entries: SitemapEntry[],\n apiUrl: string,\n apiKey: string,\n options?: SyncSitemapOptions\n): Promise<{ success: boolean; created: number; updated: number }> {\n if (!apiKey) {\n console.warn('[site-kit] No API key provided, skipping sitemap sync')\n return { success: false, created: 0, updated: 0 }\n }\n \n const normalizedEntries = entries.map(entry => {\n let path: string\n try {\n const url = new URL(entry.url)\n path = url.pathname\n } catch {\n path = entry.url.startsWith('/') ? entry.url : `/${entry.url}`\n }\n\n // Strip embedded domains from malformed paths (e.g. \"//domain.com/path\")\n path = path.replace(/^\\/{1,2}[a-z0-9.-]+\\.[a-z]{2,}(\\/|$)/i, '/')\n\n const normalized: Record<string, unknown> = {\n path,\n priority: entry.priority,\n changefreq: entry.changeFrequency,\n pageType: entry.pageType ?? inferPageType(path),\n }\n if (entry.description) {\n normalized.description = entry.description\n }\n return normalized\n }).filter(entry => {\n const p = entry.path as string\n // Reject paths that still contain embedded URLs or domains\n if (p.includes('://') || /\\/\\/[a-z0-9.-]+\\.[a-z]{2,}/i.test(p)) {\n console.warn(`[site-kit] Filtering malformed sitemap path: ${p}`)\n return false\n }\n return true\n })\n\n try {\n const body: Record<string, unknown> = { entries: normalizedEntries, mode: 'full-replace' }\n if (options?.optimizeMeta !== undefined) body.optimize_meta = options.optimizeMeta\n if (options?.awaitMeta !== undefined) body.await_meta = options.awaitMeta\n\n const response = await fetch(`${apiUrl}/api/public/seo/register-sitemap`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify(body),\n })\n \n if (!response.ok) {\n console.error('[site-kit] Sitemap sync failed:', response.status, await response.text())\n return { success: false, created: 0, updated: 0 }\n }\n \n const result = await response.json()\n \n return {\n success: true,\n created: result.created || 0,\n updated: result.updated || 0,\n }\n } catch (error) {\n console.error('[site-kit] Sitemap sync error:', error)\n return { success: false, created: 0, updated: 0 }\n }\n}\n\n/**\n * Create a sitemap generator function for Next.js\n * Automatically syncs to Portal API at build time.\n * \n * @example\n * ```ts\n * // app/sitemap.ts\n * import { createSitemap } from '@sonordev/site-kit/sitemap'\n * \n * export default createSitemap({\n * baseUrl: 'https://example.com'\n * })\n * ```\n */\n/**\n * Fetches published portfolio items from Portal API and returns them as sitemap paths.\n * Use this in createSitemap's additionalPaths to include portfolio routes.\n *\n * @example\n * ```ts\n * import { createSitemap, getPortfolioPaths } from '@sonordev/site-kit/sitemap'\n *\n * export default createSitemap({\n * baseUrl: 'https://example.com',\n * additionalPaths: async () => [\n * ...(await getPortfolioPaths()),\n * ]\n * })\n * ```\n */\nexport async function getPortfolioPaths(options?: {\n apiUrl?: string\n apiKey?: string\n basePath?: string // defaults to '/portfolio'\n priority?: number // defaults to 0.7\n}): Promise<Array<{ path: string; priority: number; changeFrequency: 'weekly' }>> {\n const apiUrl = options?.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n const apiKey = options?.apiKey || process.env.SONOR_API_KEY || process.env.UPTRADE_API_KEY || ''\n const basePath = options?.basePath || '/work'\n const priority = options?.priority || 0.7\n\n if (!apiKey) return []\n\n try {\n const response = await fetch(`${apiUrl}/api/public/portfolio/items`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n })\n\n if (!response.ok) return []\n\n const data = await response.json()\n const items = data?.portfolioItems || data?.items || data || []\n\n return items\n .filter((item: any) => item.slug && item.status === 'published')\n .map((item: any) => ({\n path: `${basePath}/${item.slug}`,\n priority,\n changeFrequency: 'weekly' as const,\n }))\n } catch {\n console.warn('[site-kit] Failed to fetch portfolio items for sitemap')\n return []\n }\n}\n\nexport function createSitemap(config: SitemapConfig): () => SitemapEntry[] | Promise<SitemapEntry[]> {\n return async () => {\n const { exclude = [], defaultChangeFrequency = 'weekly' } = config\n \n // Resolve baseUrl: from config, or from Portal via apiKey/env\n let baseUrl = config.baseUrl\n if (!baseUrl) {\n const apiKey = config.apiKey ?? process.env.SONOR_API_KEY\n const site = await getSiteConfig({ apiKey, apiUrl: config.apiUrl })\n baseUrl = site?.site_url ?? process.env.NEXT_PUBLIC_SITE_URL ?? 'https://example.com'\n }\n const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl\n \n // Default exclusions\n const allExclusions = [\n '/api/*',\n '/admin/*',\n '/_uptrade/*',\n '/sonor-setup/*',\n '/offline/*',\n ...exclude,\n ]\n \n // Discover pages from app directory (including dynamic routes)\n let pages: string[] = []\n let discoveryFailed = false\n try {\n const appDir = findAppDir()\n pages = await discoverPages(appDir, '', [], {\n dynamicRoutes: config.dynamicRoutes,\n resolveGenerateStaticParams: config.resolveGenerateStaticParams,\n })\n } catch (error) {\n console.warn('[site-kit] Failed to discover pages from filesystem:', (error as Error).message)\n // Filesystem discovery failed (typical during ISR revalidation on Netlify/Vercel\n // where the app/ directory doesn't exist in the standalone serverless bundle).\n // Fetch the page list from Portal API instead — it has the correct data from\n // the last build-time full-replace sync.\n discoveryFailed = true\n const apiKey = config.apiKey || process.env.SONOR_API_KEY || process.env.UPTRADE_API_KEY || ''\n const apiUrl = config.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n if (apiKey && normalizedBaseUrl) {\n try {\n const domain = normalizedBaseUrl.replace(/^https?:\\/\\/(www\\.)?/, '')\n const pagesResponse = await fetch(\n `${apiUrl}/api/public/seo/metadata?domain=${encodeURIComponent(domain)}`,\n { headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey } }\n )\n if (pagesResponse.ok) {\n const pagesData = await pagesResponse.json()\n const pagesList: Array<{ path: string }> = pagesData?.pagesList || []\n pages = pagesList.map(p => p.path).filter(Boolean)\n console.log(`[site-kit] Recovered ${pages.length} pages from Portal API (ISR fallback)`)\n }\n } catch (fetchErr) {\n console.warn('[site-kit] Portal API fallback failed:', (fetchErr as Error).message)\n }\n }\n if (pages.length === 0) {\n pages = ['/']\n }\n }\n \n // Normalize, deduplicate, and filter out excluded pages\n pages = Array.from(new Set(pages.map(normalizePath))).filter(page => !isExcluded(page, allExclusions))\n\n // Build sitemap entries\n const entries: SitemapEntry[] = pages.map(path => ({\n url: `${normalizedBaseUrl}${path}`,\n lastModified: new Date(),\n changeFrequency: defaultChangeFrequency,\n priority: getPriority(path, config),\n pageType: inferPageType(path),\n }))\n\n // Add additional dynamic paths if provided (deduplicating against discovered pages)\n if (config.additionalPaths) {\n try {\n // Normalize URLs for dedup so '/about' and '/about/' are treated the same\n const existingPaths = new Set(pages)\n const additional = await config.additionalPaths()\n for (const item of additional) {\n const normalizedItemPath = normalizePath(item.path)\n if (!isExcluded(normalizedItemPath, allExclusions) && !existingPaths.has(normalizedItemPath)) {\n entries.push({\n url: `${normalizedBaseUrl}${normalizedItemPath}`,\n lastModified: new Date(),\n changeFrequency: item.changeFrequency ?? defaultChangeFrequency,\n priority: item.priority ?? getPriority(normalizedItemPath, config),\n pageType: inferPageType(normalizedItemPath),\n ...(item.description ? { description: item.description } : {}),\n })\n existingPaths.add(normalizedItemPath)\n }\n }\n } catch (error) {\n console.warn('[site-kit] Failed to get additional paths:', error)\n }\n }\n \n // Intelligent priority: blend visibility scores with depth-based priority\n if (config.intelligentPriority) {\n try {\n const apiKey = config.apiKey ?? process.env.SONOR_API_KEY ?? ''\n const projectIdMatch = apiKey.match(/^(?:sonor|uptrade)_([0-9a-f-]{36})_/)\n const projectId = projectIdMatch ? projectIdMatch[1] : null\n\n if (projectId) {\n const signalApiUrl = process.env.SONOR_SIGNAL_URL || process.env.SIGNAL_API_URL || process.env.NEXT_PUBLIC_SIGNAL_API_URL || 'https://signal.sonor.io'\n const visResponse = await fetch(`${signalApiUrl}/skills/seo/visibility/${projectId}`, {\n headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },\n }).catch(() => null)\n\n if (visResponse?.ok) {\n const visData = await visResponse.json()\n const scores = (visData?.data || visData) as Array<{ page_path: string; overall_score: number }>\n const scoreMap = new Map(scores.map(s => [s.page_path, s.overall_score]))\n\n let appliedCount = 0\n for (const entry of entries) {\n const path = new URL(entry.url).pathname\n const visScore = scoreMap.get(path)\n if (visScore !== undefined) {\n const depthBonus = path === '/' ? 1.0 : Math.max(0.3, 1.0 - (path.split('/').filter(Boolean).length * 0.15))\n // 40% visibility + 30% depth + 30% base\n entry.priority = Math.round((visScore * 0.4 + depthBonus * 0.3 + (entry.priority ?? 0.5) * 0.3) * 100) / 100\n appliedCount++\n }\n }\n if (appliedCount > 0) {\n console.log(`[site-kit] Applied intelligent priority from ${appliedCount} visibility scores`)\n }\n }\n }\n } catch {\n // Signal API unavailable — fall back to static priority\n }\n }\n\n // Sort by priority descending\n entries.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))\n\n console.log(`[site-kit] Generated sitemap with ${entries.length} pages`)\n \n // Auto-sync to Portal API at build time only (skip in dev — sitemap.xml is re-rendered on every request)\n // Also skip sync when page discovery failed (e.g. ISR revalidation where the app/ directory\n // doesn't exist in the standalone serverless bundle). Running full-replace with only\n // additionalPaths would DELETE all properly-synced static routes discovered at build time.\n const isDev = process.env.NODE_ENV === 'development'\n if (!config.disableSync && !isDev && !discoveryFailed) {\n const apiUrl = config.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n const apiKey = config.apiKey || process.env.SONOR_API_KEY\n \n if (apiKey) {\n try {\n const syncOptions: SyncSitemapOptions = {}\n if (config.awaitMetaOptimization === true) {\n syncOptions.optimizeMeta = true\n syncOptions.awaitMeta = true\n }\n const result = await syncSitemapToPortal(entries, apiUrl, apiKey, syncOptions)\n if (result.success) {\n console.log(`[site-kit] Synced to Portal API: ${result.created} created, ${result.updated} updated`)\n\n // Verification: report meta optimization coverage after sync\n if (config.awaitMetaOptimization === true) {\n try {\n const domain = normalizedBaseUrl.replace(/^https?:\\/\\/(www\\.)?/, '')\n const pagesResponse = await fetch(\n `${apiUrl}/api/public/seo/metadata?domain=${encodeURIComponent(domain)}`,\n { headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey } }\n )\n if (pagesResponse.ok) {\n const pagesData = await pagesResponse.json()\n const allPages: Array<{ managed_title?: string; managed_meta_description?: string }> = pagesData?.pagesList || []\n const optimized = allPages.filter(p => p.managed_title && p.managed_meta_description)\n console.log(`[site-kit] Meta optimization coverage: ${optimized.length}/${allPages.length} pages (${allPages.length ? Math.round((optimized.length / allPages.length) * 100) : 0}%)`)\n }\n } catch {\n console.warn('[site-kit] Could not verify meta optimization coverage')\n }\n }\n }\n\n // Write AI-optimized llms.txt after sync (default true when API key present)\n if (config.optimizedLLMsTxt !== false) {\n try {\n const writeResult = await writeLLMsTxtToPublic({\n apiUrl,\n apiKey,\n full: config.optimizedLLMsFullTxt ?? false,\n fallbackToLocal: true,\n getLocalData: config.getLocalData,\n })\n if (writeResult.success) {\n console.log(`[site-kit] Wrote llms.txt (optimized=${writeResult.optimized})`)\n }\n } catch (err) {\n console.warn('[site-kit] Failed to write llms.txt:', (err as Error).message)\n }\n }\n } catch (err) {\n console.warn('[site-kit] Failed to sync sitemap to Portal API:', (err as Error).message)\n }\n } else {\n console.log('[site-kit] No API key found, skipping Portal API sync')\n }\n }\n \n return entries\n }\n}\n"]}
|
package/dist/sitemap/index.mjs
CHANGED
|
@@ -73,11 +73,7 @@ async function tryGenerateStaticParams(pageFilePath) {
|
|
|
73
73
|
return params;
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
} catch
|
|
77
|
-
console.warn(
|
|
78
|
-
`[site-kit] Could not resolve generateStaticParams from ${pageFilePath}:`,
|
|
79
|
-
err.message
|
|
80
|
-
);
|
|
76
|
+
} catch {
|
|
81
77
|
}
|
|
82
78
|
return null;
|
|
83
79
|
}
|
|
@@ -148,8 +144,8 @@ async function discoverPages(appDir, currentPath = "", pages = [], options = {})
|
|
|
148
144
|
}
|
|
149
145
|
}
|
|
150
146
|
} else {
|
|
151
|
-
console.
|
|
152
|
-
`[site-kit]
|
|
147
|
+
console.debug(
|
|
148
|
+
`[site-kit] Dynamic segment "${entry}" at /${currentPath} not resolved via filesystem. If these routes are missing from sitemap, add them via additionalPaths or dynamicRoutes config.`
|
|
153
149
|
);
|
|
154
150
|
}
|
|
155
151
|
continue;
|
|
@@ -260,6 +256,7 @@ async function syncSitemapToPortal(entries, apiUrl, apiKey, options) {
|
|
|
260
256
|
} catch {
|
|
261
257
|
path = entry.url.startsWith("/") ? entry.url : `/${entry.url}`;
|
|
262
258
|
}
|
|
259
|
+
path = path.replace(/^\/{1,2}[a-z0-9.-]+\.[a-z]{2,}(\/|$)/i, "/");
|
|
263
260
|
const normalized = {
|
|
264
261
|
path,
|
|
265
262
|
priority: entry.priority,
|
|
@@ -270,6 +267,13 @@ async function syncSitemapToPortal(entries, apiUrl, apiKey, options) {
|
|
|
270
267
|
normalized.description = entry.description;
|
|
271
268
|
}
|
|
272
269
|
return normalized;
|
|
270
|
+
}).filter((entry) => {
|
|
271
|
+
const p = entry.path;
|
|
272
|
+
if (p.includes("://") || /\/\/[a-z0-9.-]+\.[a-z]{2,}/i.test(p)) {
|
|
273
|
+
console.warn(`[site-kit] Filtering malformed sitemap path: ${p}`);
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
return true;
|
|
273
277
|
});
|
|
274
278
|
try {
|
|
275
279
|
const body = { entries: normalizedEntries, mode: "full-replace" };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/sitemap/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @sonordev/site-kit - Sitemap Generator\n * \n * Automatically generates sitemap.xml from Next.js app directory structure.\n * Works at build time to discover all pages and sync them to Portal API.\n * \n * @example\n * ```ts\n * // app/sitemap.ts\n * import { createSitemap } from '@sonordev/site-kit/sitemap'\n * \n * export default createSitemap({\n * baseUrl: 'https://example.com',\n * // Optional: exclude patterns\n * exclude: ['/admin/*', '/api/*'],\n * // Optional: additional dynamic routes\n * additionalPaths: async () => [\n * { path: '/blog/post-1', priority: 0.7 },\n * ]\n * })\n * ```\n */\n\nimport { readdirSync, statSync, existsSync } from 'fs'\nimport { join, resolve } from 'path'\nimport { pathToFileURL } from 'url'\nimport { writeLLMsTxtToPublic } from '../llms/writeLLMsTxt'\nimport { getSiteConfig } from '../site-config'\n\nexport type PageType = 'homepage' | 'service' | 'comparison' | 'blog' | 'industry' | 'portfolio' | 'faq' | 'pricing' | 'page'\n\nexport interface SitemapEntry {\n url: string\n lastModified?: Date | string\n changeFrequency?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'\n priority?: number\n /** Optional page description for Signal context */\n description?: string\n /** Inferred or explicit page type for Signal context */\n pageType?: PageType\n}\n\nexport interface SitemapConfig {\n /** Base URL for the site (optional when apiKey is set; then resolved from Portal project-info) */\n baseUrl?: string\n /** Glob patterns to exclude (e.g., ['/admin/*', '/api/*']) */\n exclude?: string[]\n /** Default priority for pages */\n defaultPriority?: number\n /** Default change frequency */\n defaultChangeFrequency?: SitemapEntry['changeFrequency']\n /** Additional paths to include (for dynamic routes) */\n additionalPaths?: () => Promise<Array<{ path: string; priority?: number; changeFrequency?: SitemapEntry['changeFrequency']; description?: string }>>\n /**\n * Explicit values for dynamic route segments.\n * Keys are the segment names (e.g. '[slug]') and values are arrays of param values.\n * This is a simpler alternative to relying on generateStaticParams auto-discovery.\n * @example { '[slug]': ['seo', 'analytics', 'crm', 'echo'] }\n */\n dynamicRoutes?: Record<string, string[]>\n /** When true (default), attempt to import generateStaticParams from dynamic route pages at build time */\n resolveGenerateStaticParams?: boolean\n /** Priority overrides by path pattern */\n priorities?: Record<string, number>\n /** Portal API URL for syncing (defaults to SONOR_API_URL or https://api.sonor.io) */\n apiUrl?: string\n /** Portal API key for syncing (defaults to SONOR_API_KEY) */\n apiKey?: string\n /** Disable auto-sync to Portal API */\n disableSync?: boolean\n /** When true, await meta optimization before build completes (for build-time llms.txt with managed_llm_schema) */\n awaitMetaOptimization?: boolean\n /** When true (default when API key present), write AI-optimized llms.txt to public/llms.txt after sitemap sync */\n optimizedLLMsTxt?: boolean\n /** When true with optimizedLLMsTxt, also write public/llms-full.txt */\n optimizedLLMsFullTxt?: boolean\n /** When Portal returns empty services, use this to supply local site data for llms.txt */\n getLocalData?: () => Promise<import('../llms/types').LLMsDataResponse | null>\n /** Use Signal AI visibility scores to compute page priority (40% visibility + 30% depth + 30% base) */\n intelligentPriority?: boolean\n}\n\n// Route groups and special folders to ignore\nconst IGNORED_FOLDERS = [\n 'api',\n 'admin',\n '_uptrade',\n '%5Fuptrade', // URL-encoded _uptrade\n 'sonor-setup',\n 'offline',\n]\n\n// File patterns that indicate a page\nconst PAGE_FILES = ['page.tsx', 'page.jsx', 'page.js', 'page.ts']\n\n/**\n * Strip trailing slash from a path (preserving '/' root).\n * Used to normalize paths before comparison so that '/about' and '/about/' are treated the same.\n */\nfunction normalizePath(path: string): string {\n if (path !== '/' && path.endsWith('/')) {\n return path.slice(0, -1)\n }\n return path\n}\n\n/**\n * Check if a path matches any exclude pattern.\n * Normalizes both the path and pattern prefixes to handle trailing slash inconsistencies.\n */\nfunction isExcluded(path: string, patterns: string[]): boolean {\n const normalized = normalizePath(path)\n return patterns.some(pattern => {\n if (pattern.endsWith('/*')) {\n const prefix = normalizePath(pattern.slice(0, -2))\n return normalized === prefix || normalized.startsWith(prefix + '/')\n }\n if (pattern.endsWith('*')) {\n const prefix = pattern.slice(0, -1)\n return normalized.startsWith(prefix)\n }\n return normalized === normalizePath(pattern)\n })\n}\n\n/**\n * Infer page type from URL path pattern.\n * Used to give Signal context about what each page covers.\n */\nfunction inferPageType(urlPath: string): PageType {\n if (urlPath === '/') return 'homepage'\n if (/^\\/services(\\/|$)/i.test(urlPath)) return 'service'\n if (/^\\/compare(\\/|$)/i.test(urlPath)) return 'comparison'\n if (/^\\/(insights|blog)(\\/|$)/i.test(urlPath)) return 'blog'\n if (/^\\/industries(\\/|$)/i.test(urlPath)) return 'industry'\n if (/^\\/work(\\/|$)/i.test(urlPath)) return 'portfolio'\n if (/^\\/faq(\\/|$)/i.test(urlPath)) return 'faq'\n if (/^\\/pricing(\\/|$)/i.test(urlPath)) return 'pricing'\n return 'page'\n}\n\n/**\n * Convert a file-system path (relative to appDir) into a URL path,\n * stripping route groups and cleaning slashes.\n */\nfunction toUrlPath(fsPath: string): string {\n let urlPath = '/' + fsPath\n // Handle route groups (parentheses) - remove them from URL\n urlPath = urlPath.replace(/\\/\\([^)]+\\)/g, '')\n // Clean up path\n urlPath = urlPath.replace(/\\/+/g, '/') // Remove double slashes\n if (urlPath !== '/' && urlPath.endsWith('/')) {\n urlPath = urlPath.slice(0, -1) // Remove trailing slash\n }\n return urlPath\n}\n\n/**\n * Attempt to call generateStaticParams from a dynamic route page module.\n * Returns the param objects, or null if unavailable.\n */\nasync function tryGenerateStaticParams(\n pageFilePath: string\n): Promise<Array<Record<string, string>> | null> {\n try {\n const fileUrl = pathToFileURL(pageFilePath).href\n // Use a timeout to guard against slow external API calls\n const importPromise = import(fileUrl)\n const timeoutPromise = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('generateStaticParams import timed out (5s)')), 5000)\n )\n const mod = await Promise.race([importPromise, timeoutPromise])\n\n if (typeof mod.generateStaticParams === 'function') {\n const resultPromise = Promise.resolve(mod.generateStaticParams())\n const resultTimeout = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('generateStaticParams call timed out (10s)')), 10000)\n )\n const params = await Promise.race([resultPromise, resultTimeout])\n if (Array.isArray(params)) {\n return params as Array<Record<string, string>>\n }\n }\n } catch (err) {\n console.warn(\n `[site-kit] Could not resolve generateStaticParams from ${pageFilePath}:`,\n (err as Error).message\n )\n }\n return null\n}\n\ninterface DiscoverPagesOptions {\n /** Explicit param values for dynamic segments, keyed by segment name e.g. '[slug]' */\n dynamicRoutes?: Record<string, string[]>\n /** Whether to attempt auto-import of generateStaticParams (default true) */\n resolveGenerateStaticParams?: boolean\n}\n\n/**\n * Recursively discover pages from the app directory.\n * Handles dynamic route segments via explicit config or generateStaticParams auto-discovery.\n */\nasync function discoverPages(\n appDir: string,\n currentPath: string = '',\n pages: string[] = [],\n options: DiscoverPagesOptions = {}\n): Promise<string[]> {\n const fullPath = join(appDir, currentPath)\n\n if (!existsSync(fullPath)) {\n return pages\n }\n\n const entries = readdirSync(fullPath)\n\n // Check if this directory has a page file\n const hasPage = entries.some(entry => PAGE_FILES.includes(entry))\n\n if (hasPage) {\n const urlPath = toUrlPath(currentPath)\n // Only add if the path has no unresolved dynamic segments\n if (!urlPath.includes('[')) {\n pages.push(urlPath)\n }\n }\n\n // Recurse into subdirectories\n for (const entry of entries) {\n const entryPath = join(fullPath, entry)\n const stat = statSync(entryPath)\n\n if (stat.isDirectory()) {\n // Skip ignored folders\n const folderName = entry.toLowerCase()\n if (IGNORED_FOLDERS.some(ignored => folderName.includes(ignored))) {\n continue\n }\n\n // Skip private folders\n if (entry.startsWith('_')) {\n continue\n }\n\n // Handle dynamic route segments\n if (entry.startsWith('[') && entry.endsWith(']')) {\n const paramName = entry.slice(1, -1) // e.g. 'slug' from '[slug]'\n let paramValues: string[] | null = null\n\n // 1. Check explicit dynamicRoutes config first\n if (options.dynamicRoutes) {\n // Match by full segment name '[slug]' or bare param name 'slug'\n paramValues = options.dynamicRoutes[entry] ?? options.dynamicRoutes[paramName] ?? null\n }\n\n // 2. Fall back to auto-importing generateStaticParams\n if (!paramValues && options.resolveGenerateStaticParams !== false) {\n // Find the page file inside the dynamic directory\n const dynamicDirPath = join(fullPath, entry)\n const pageFile = PAGE_FILES.find(pf => existsSync(join(dynamicDirPath, pf)))\n\n if (pageFile) {\n const pageFilePath = resolve(dynamicDirPath, pageFile)\n const params = await tryGenerateStaticParams(pageFilePath)\n if (params && params.length > 0) {\n // Extract the values for this specific param\n paramValues = params\n .map(p => p[paramName])\n .filter((v): v is string => typeof v === 'string')\n }\n }\n }\n\n if (paramValues && paramValues.length > 0) {\n const dynamicDirPath = join(fullPath, entry)\n const dynamicEntries = readdirSync(dynamicDirPath)\n\n // Check if the dynamic directory itself has a page file\n const dynamicHasPage = dynamicEntries.some(e => PAGE_FILES.includes(e))\n\n for (const value of paramValues) {\n // If the dynamic dir has a page, add the resolved path\n if (dynamicHasPage) {\n const resolvedPath = toUrlPath(join(currentPath, value))\n if (!resolvedPath.includes('[')) {\n pages.push(resolvedPath)\n }\n }\n\n // Recurse into subdirectories inside the dynamic segment,\n // mapping [param] on disk to the resolved value in the URL\n for (const subEntry of dynamicEntries) {\n if (PAGE_FILES.includes(subEntry)) continue // skip page files\n const subEntryPath = join(dynamicDirPath, subEntry)\n if (statSync(subEntryPath).isDirectory()) {\n // For nested dynamic segments or regular subdirs, recurse\n // We need a custom appDir-relative path that uses the resolved value\n // but reads from the dynamic dir on disk\n await discoverPagesFromDisk(\n dynamicDirPath,\n subEntry,\n join(currentPath, value),\n pages,\n options\n )\n }\n }\n }\n } else {\n console.warn(\n `[site-kit] Skipping dynamic segment \"${entry}\" at /${currentPath} — no params resolved. ` +\n `Provide dynamicRoutes config or export generateStaticParams from the page.`\n )\n }\n continue\n }\n\n await discoverPages(appDir, join(currentPath, entry), pages, options)\n }\n }\n\n return pages\n}\n\n/**\n * Discover pages starting from a disk path that differs from the URL path.\n * Used when traversing inside dynamic route segments where the filesystem\n * directory name (e.g. [slug]) differs from the URL segment (e.g. 'seo').\n */\nasync function discoverPagesFromDisk(\n diskParent: string,\n diskEntry: string,\n urlParentPath: string,\n pages: string[],\n options: DiscoverPagesOptions\n): Promise<void> {\n const diskPath = join(diskParent, diskEntry)\n if (!existsSync(diskPath) || !statSync(diskPath).isDirectory()) return\n\n const entries = readdirSync(diskPath)\n const hasPage = entries.some(e => PAGE_FILES.includes(e))\n const urlPath = toUrlPath(join(urlParentPath, diskEntry))\n\n if (hasPage && !urlPath.includes('[')) {\n pages.push(urlPath)\n }\n\n for (const sub of entries) {\n if (PAGE_FILES.includes(sub)) continue\n const subPath = join(diskPath, sub)\n if (!statSync(subPath).isDirectory()) continue\n\n const folderName = sub.toLowerCase()\n if (IGNORED_FOLDERS.some(ignored => folderName.includes(ignored))) continue\n if (sub.startsWith('_')) continue\n\n if (sub.startsWith('[') && sub.endsWith(']')) {\n // Nested dynamic segment — resolve its params too\n const nestedParam = sub.slice(1, -1)\n let nestedValues: string[] | null = null\n\n if (options.dynamicRoutes) {\n nestedValues = options.dynamicRoutes[sub] ?? options.dynamicRoutes[nestedParam] ?? null\n }\n\n if (!nestedValues && options.resolveGenerateStaticParams !== false) {\n const nestedDir = join(diskPath, sub)\n const pageFile = PAGE_FILES.find(pf => existsSync(join(nestedDir, pf)))\n if (pageFile) {\n const params = await tryGenerateStaticParams(resolve(nestedDir, pageFile))\n if (params && params.length > 0) {\n nestedValues = params.map(p => p[nestedParam]).filter((v): v is string => typeof v === 'string')\n }\n }\n }\n\n if (nestedValues && nestedValues.length > 0) {\n const nestedDir = join(diskPath, sub)\n const nestedEntries = readdirSync(nestedDir)\n const nestedHasPage = nestedEntries.some(e => PAGE_FILES.includes(e))\n for (const val of nestedValues) {\n if (nestedHasPage) {\n const resolved = toUrlPath(join(urlParentPath, diskEntry, val))\n if (!resolved.includes('[')) pages.push(resolved)\n }\n for (const nestedSub of nestedEntries) {\n if (PAGE_FILES.includes(nestedSub)) continue\n const nestedSubPath = join(nestedDir, nestedSub)\n if (statSync(nestedSubPath).isDirectory()) {\n await discoverPagesFromDisk(nestedDir, nestedSub, join(urlParentPath, diskEntry, val), pages, options)\n }\n }\n }\n }\n continue\n }\n\n await discoverPagesFromDisk(diskPath, sub, join(urlParentPath, diskEntry), pages, options)\n }\n}\n\n/**\n * Find the app directory from the current working directory\n */\nfunction findAppDir(): string {\n const cwd = process.cwd()\n \n // Try common locations\n const candidates = [\n join(cwd, 'app'),\n join(cwd, 'src', 'app'),\n ]\n \n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate\n }\n }\n \n throw new Error(\n '[site-kit] Could not find app directory. Ensure you have an \"app\" or \"src/app\" folder.'\n )\n}\n\n/**\n * Get priority for a path based on config\n */\nfunction getPriority(path: string, config: SitemapConfig): number {\n const normalized = normalizePath(path)\n // Check priority overrides\n if (config.priorities) {\n for (const [pattern, priority] of Object.entries(config.priorities)) {\n if (pattern.endsWith('/*')) {\n const prefix = normalizePath(pattern.slice(0, -2))\n if (normalized === prefix || normalized.startsWith(prefix + '/')) return priority\n } else if (normalized === normalizePath(pattern)) {\n return priority\n }\n }\n }\n \n // Home page gets highest priority\n if (path === '/') return 1.0\n \n // Shallow pages get higher priority\n const depth = path.split('/').filter(Boolean).length\n if (depth === 1) return 0.8\n if (depth === 2) return 0.6\n \n return config.defaultPriority ?? 0.5\n}\n\nexport interface SyncSitemapOptions {\n /** Trigger Signal AI meta optimization after sync */\n optimizeMeta?: boolean\n /** When true, wait for meta optimization to complete (for build-time llms.txt) */\n awaitMeta?: boolean\n}\n\n/**\n * Sync sitemap entries to Portal API\n * Called automatically by createSitemap at build time\n */\nexport async function syncSitemapToPortal(\n entries: SitemapEntry[],\n apiUrl: string,\n apiKey: string,\n options?: SyncSitemapOptions\n): Promise<{ success: boolean; created: number; updated: number }> {\n if (!apiKey) {\n console.warn('[site-kit] No API key provided, skipping sitemap sync')\n return { success: false, created: 0, updated: 0 }\n }\n \n const normalizedEntries = entries.map(entry => {\n let path: string\n try {\n const url = new URL(entry.url)\n path = url.pathname\n } catch {\n path = entry.url.startsWith('/') ? entry.url : `/${entry.url}`\n }\n\n const normalized: Record<string, unknown> = {\n path,\n priority: entry.priority,\n changefreq: entry.changeFrequency,\n pageType: entry.pageType ?? inferPageType(path),\n }\n if (entry.description) {\n normalized.description = entry.description\n }\n return normalized\n })\n \n try {\n const body: Record<string, unknown> = { entries: normalizedEntries, mode: 'full-replace' }\n if (options?.optimizeMeta !== undefined) body.optimize_meta = options.optimizeMeta\n if (options?.awaitMeta !== undefined) body.await_meta = options.awaitMeta\n\n const response = await fetch(`${apiUrl}/api/public/seo/register-sitemap`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify(body),\n })\n \n if (!response.ok) {\n console.error('[site-kit] Sitemap sync failed:', response.status, await response.text())\n return { success: false, created: 0, updated: 0 }\n }\n \n const result = await response.json()\n \n return {\n success: true,\n created: result.created || 0,\n updated: result.updated || 0,\n }\n } catch (error) {\n console.error('[site-kit] Sitemap sync error:', error)\n return { success: false, created: 0, updated: 0 }\n }\n}\n\n/**\n * Create a sitemap generator function for Next.js\n * Automatically syncs to Portal API at build time.\n * \n * @example\n * ```ts\n * // app/sitemap.ts\n * import { createSitemap } from '@sonordev/site-kit/sitemap'\n * \n * export default createSitemap({\n * baseUrl: 'https://example.com'\n * })\n * ```\n */\n/**\n * Fetches published portfolio items from Portal API and returns them as sitemap paths.\n * Use this in createSitemap's additionalPaths to include portfolio routes.\n *\n * @example\n * ```ts\n * import { createSitemap, getPortfolioPaths } from '@sonordev/site-kit/sitemap'\n *\n * export default createSitemap({\n * baseUrl: 'https://example.com',\n * additionalPaths: async () => [\n * ...(await getPortfolioPaths()),\n * ]\n * })\n * ```\n */\nexport async function getPortfolioPaths(options?: {\n apiUrl?: string\n apiKey?: string\n basePath?: string // defaults to '/portfolio'\n priority?: number // defaults to 0.7\n}): Promise<Array<{ path: string; priority: number; changeFrequency: 'weekly' }>> {\n const apiUrl = options?.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n const apiKey = options?.apiKey || process.env.SONOR_API_KEY || process.env.UPTRADE_API_KEY || ''\n const basePath = options?.basePath || '/work'\n const priority = options?.priority || 0.7\n\n if (!apiKey) return []\n\n try {\n const response = await fetch(`${apiUrl}/api/public/portfolio/items`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n })\n\n if (!response.ok) return []\n\n const data = await response.json()\n const items = data?.portfolioItems || data?.items || data || []\n\n return items\n .filter((item: any) => item.slug && item.status === 'published')\n .map((item: any) => ({\n path: `${basePath}/${item.slug}`,\n priority,\n changeFrequency: 'weekly' as const,\n }))\n } catch {\n console.warn('[site-kit] Failed to fetch portfolio items for sitemap')\n return []\n }\n}\n\nexport function createSitemap(config: SitemapConfig): () => SitemapEntry[] | Promise<SitemapEntry[]> {\n return async () => {\n const { exclude = [], defaultChangeFrequency = 'weekly' } = config\n \n // Resolve baseUrl: from config, or from Portal via apiKey/env\n let baseUrl = config.baseUrl\n if (!baseUrl) {\n const apiKey = config.apiKey ?? process.env.SONOR_API_KEY\n const site = await getSiteConfig({ apiKey, apiUrl: config.apiUrl })\n baseUrl = site?.site_url ?? process.env.NEXT_PUBLIC_SITE_URL ?? 'https://example.com'\n }\n const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl\n \n // Default exclusions\n const allExclusions = [\n '/api/*',\n '/admin/*',\n '/_uptrade/*',\n '/sonor-setup/*',\n '/offline/*',\n ...exclude,\n ]\n \n // Discover pages from app directory (including dynamic routes)\n let pages: string[] = []\n let discoveryFailed = false\n try {\n const appDir = findAppDir()\n pages = await discoverPages(appDir, '', [], {\n dynamicRoutes: config.dynamicRoutes,\n resolveGenerateStaticParams: config.resolveGenerateStaticParams,\n })\n } catch (error) {\n console.warn('[site-kit] Failed to discover pages from filesystem:', (error as Error).message)\n // Filesystem discovery failed (typical during ISR revalidation on Netlify/Vercel\n // where the app/ directory doesn't exist in the standalone serverless bundle).\n // Fetch the page list from Portal API instead — it has the correct data from\n // the last build-time full-replace sync.\n discoveryFailed = true\n const apiKey = config.apiKey || process.env.SONOR_API_KEY || process.env.UPTRADE_API_KEY || ''\n const apiUrl = config.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n if (apiKey && normalizedBaseUrl) {\n try {\n const domain = normalizedBaseUrl.replace(/^https?:\\/\\/(www\\.)?/, '')\n const pagesResponse = await fetch(\n `${apiUrl}/api/public/seo/metadata?domain=${encodeURIComponent(domain)}`,\n { headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey } }\n )\n if (pagesResponse.ok) {\n const pagesData = await pagesResponse.json()\n const pagesList: Array<{ path: string }> = pagesData?.pagesList || []\n pages = pagesList.map(p => p.path).filter(Boolean)\n console.log(`[site-kit] Recovered ${pages.length} pages from Portal API (ISR fallback)`)\n }\n } catch (fetchErr) {\n console.warn('[site-kit] Portal API fallback failed:', (fetchErr as Error).message)\n }\n }\n if (pages.length === 0) {\n pages = ['/']\n }\n }\n \n // Normalize, deduplicate, and filter out excluded pages\n pages = Array.from(new Set(pages.map(normalizePath))).filter(page => !isExcluded(page, allExclusions))\n\n // Build sitemap entries\n const entries: SitemapEntry[] = pages.map(path => ({\n url: `${normalizedBaseUrl}${path}`,\n lastModified: new Date(),\n changeFrequency: defaultChangeFrequency,\n priority: getPriority(path, config),\n pageType: inferPageType(path),\n }))\n\n // Add additional dynamic paths if provided (deduplicating against discovered pages)\n if (config.additionalPaths) {\n try {\n // Normalize URLs for dedup so '/about' and '/about/' are treated the same\n const existingPaths = new Set(pages)\n const additional = await config.additionalPaths()\n for (const item of additional) {\n const normalizedItemPath = normalizePath(item.path)\n if (!isExcluded(normalizedItemPath, allExclusions) && !existingPaths.has(normalizedItemPath)) {\n entries.push({\n url: `${normalizedBaseUrl}${normalizedItemPath}`,\n lastModified: new Date(),\n changeFrequency: item.changeFrequency ?? defaultChangeFrequency,\n priority: item.priority ?? getPriority(normalizedItemPath, config),\n pageType: inferPageType(normalizedItemPath),\n ...(item.description ? { description: item.description } : {}),\n })\n existingPaths.add(normalizedItemPath)\n }\n }\n } catch (error) {\n console.warn('[site-kit] Failed to get additional paths:', error)\n }\n }\n \n // Intelligent priority: blend visibility scores with depth-based priority\n if (config.intelligentPriority) {\n try {\n const apiKey = config.apiKey ?? process.env.SONOR_API_KEY ?? ''\n const projectIdMatch = apiKey.match(/^(?:sonor|uptrade)_([0-9a-f-]{36})_/)\n const projectId = projectIdMatch ? projectIdMatch[1] : null\n\n if (projectId) {\n const signalApiUrl = process.env.SONOR_SIGNAL_URL || process.env.SIGNAL_API_URL || process.env.NEXT_PUBLIC_SIGNAL_API_URL || 'https://signal.sonor.io'\n const visResponse = await fetch(`${signalApiUrl}/skills/seo/visibility/${projectId}`, {\n headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },\n }).catch(() => null)\n\n if (visResponse?.ok) {\n const visData = await visResponse.json()\n const scores = (visData?.data || visData) as Array<{ page_path: string; overall_score: number }>\n const scoreMap = new Map(scores.map(s => [s.page_path, s.overall_score]))\n\n let appliedCount = 0\n for (const entry of entries) {\n const path = new URL(entry.url).pathname\n const visScore = scoreMap.get(path)\n if (visScore !== undefined) {\n const depthBonus = path === '/' ? 1.0 : Math.max(0.3, 1.0 - (path.split('/').filter(Boolean).length * 0.15))\n // 40% visibility + 30% depth + 30% base\n entry.priority = Math.round((visScore * 0.4 + depthBonus * 0.3 + (entry.priority ?? 0.5) * 0.3) * 100) / 100\n appliedCount++\n }\n }\n if (appliedCount > 0) {\n console.log(`[site-kit] Applied intelligent priority from ${appliedCount} visibility scores`)\n }\n }\n }\n } catch {\n // Signal API unavailable — fall back to static priority\n }\n }\n\n // Sort by priority descending\n entries.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))\n\n console.log(`[site-kit] Generated sitemap with ${entries.length} pages`)\n \n // Auto-sync to Portal API at build time only (skip in dev — sitemap.xml is re-rendered on every request)\n // Also skip sync when page discovery failed (e.g. ISR revalidation where the app/ directory\n // doesn't exist in the standalone serverless bundle). Running full-replace with only\n // additionalPaths would DELETE all properly-synced static routes discovered at build time.\n const isDev = process.env.NODE_ENV === 'development'\n if (!config.disableSync && !isDev && !discoveryFailed) {\n const apiUrl = config.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n const apiKey = config.apiKey || process.env.SONOR_API_KEY\n \n if (apiKey) {\n try {\n const syncOptions: SyncSitemapOptions = {}\n if (config.awaitMetaOptimization === true) {\n syncOptions.optimizeMeta = true\n syncOptions.awaitMeta = true\n }\n const result = await syncSitemapToPortal(entries, apiUrl, apiKey, syncOptions)\n if (result.success) {\n console.log(`[site-kit] Synced to Portal API: ${result.created} created, ${result.updated} updated`)\n\n // Verification: report meta optimization coverage after sync\n if (config.awaitMetaOptimization === true) {\n try {\n const domain = normalizedBaseUrl.replace(/^https?:\\/\\/(www\\.)?/, '')\n const pagesResponse = await fetch(\n `${apiUrl}/api/public/seo/metadata?domain=${encodeURIComponent(domain)}`,\n { headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey } }\n )\n if (pagesResponse.ok) {\n const pagesData = await pagesResponse.json()\n const allPages: Array<{ managed_title?: string; managed_meta_description?: string }> = pagesData?.pagesList || []\n const optimized = allPages.filter(p => p.managed_title && p.managed_meta_description)\n console.log(`[site-kit] Meta optimization coverage: ${optimized.length}/${allPages.length} pages (${allPages.length ? Math.round((optimized.length / allPages.length) * 100) : 0}%)`)\n }\n } catch {\n console.warn('[site-kit] Could not verify meta optimization coverage')\n }\n }\n }\n\n // Write AI-optimized llms.txt after sync (default true when API key present)\n if (config.optimizedLLMsTxt !== false) {\n try {\n const writeResult = await writeLLMsTxtToPublic({\n apiUrl,\n apiKey,\n full: config.optimizedLLMsFullTxt ?? false,\n fallbackToLocal: true,\n getLocalData: config.getLocalData,\n })\n if (writeResult.success) {\n console.log(`[site-kit] Wrote llms.txt (optimized=${writeResult.optimized})`)\n }\n } catch (err) {\n console.warn('[site-kit] Failed to write llms.txt:', (err as Error).message)\n }\n }\n } catch (err) {\n console.warn('[site-kit] Failed to sync sitemap to Portal API:', (err as Error).message)\n }\n } else {\n console.log('[site-kit] No API key found, skipping Portal API sync')\n }\n }\n \n return entries\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/sitemap/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @sonordev/site-kit - Sitemap Generator\n * \n * Automatically generates sitemap.xml from Next.js app directory structure.\n * Works at build time to discover all pages and sync them to Portal API.\n * \n * @example\n * ```ts\n * // app/sitemap.ts\n * import { createSitemap } from '@sonordev/site-kit/sitemap'\n * \n * export default createSitemap({\n * baseUrl: 'https://example.com',\n * // Optional: exclude patterns\n * exclude: ['/admin/*', '/api/*'],\n * // Optional: additional dynamic routes\n * additionalPaths: async () => [\n * { path: '/blog/post-1', priority: 0.7 },\n * ]\n * })\n * ```\n */\n\nimport { readdirSync, statSync, existsSync } from 'fs'\nimport { join, resolve } from 'path'\nimport { pathToFileURL } from 'url'\nimport { writeLLMsTxtToPublic } from '../llms/writeLLMsTxt'\nimport { getSiteConfig } from '../site-config'\n\nexport type PageType = 'homepage' | 'service' | 'comparison' | 'blog' | 'industry' | 'portfolio' | 'faq' | 'pricing' | 'page'\n\nexport interface SitemapEntry {\n url: string\n lastModified?: Date | string\n changeFrequency?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'\n priority?: number\n /** Optional page description for Signal context */\n description?: string\n /** Inferred or explicit page type for Signal context */\n pageType?: PageType\n}\n\nexport interface SitemapConfig {\n /** Base URL for the site (optional when apiKey is set; then resolved from Portal project-info) */\n baseUrl?: string\n /** Glob patterns to exclude (e.g., ['/admin/*', '/api/*']) */\n exclude?: string[]\n /** Default priority for pages */\n defaultPriority?: number\n /** Default change frequency */\n defaultChangeFrequency?: SitemapEntry['changeFrequency']\n /** Additional paths to include (for dynamic routes) */\n additionalPaths?: () => Promise<Array<{ path: string; priority?: number; changeFrequency?: SitemapEntry['changeFrequency']; description?: string }>>\n /**\n * Explicit values for dynamic route segments.\n * Keys are the segment names (e.g. '[slug]') and values are arrays of param values.\n * This is a simpler alternative to relying on generateStaticParams auto-discovery.\n * @example { '[slug]': ['seo', 'analytics', 'crm', 'echo'] }\n */\n dynamicRoutes?: Record<string, string[]>\n /** When true (default), attempt to import generateStaticParams from dynamic route pages at build time */\n resolveGenerateStaticParams?: boolean\n /** Priority overrides by path pattern */\n priorities?: Record<string, number>\n /** Portal API URL for syncing (defaults to SONOR_API_URL or https://api.sonor.io) */\n apiUrl?: string\n /** Portal API key for syncing (defaults to SONOR_API_KEY) */\n apiKey?: string\n /** Disable auto-sync to Portal API */\n disableSync?: boolean\n /** When true, await meta optimization before build completes (for build-time llms.txt with managed_llm_schema) */\n awaitMetaOptimization?: boolean\n /** When true (default when API key present), write AI-optimized llms.txt to public/llms.txt after sitemap sync */\n optimizedLLMsTxt?: boolean\n /** When true with optimizedLLMsTxt, also write public/llms-full.txt */\n optimizedLLMsFullTxt?: boolean\n /** When Portal returns empty services, use this to supply local site data for llms.txt */\n getLocalData?: () => Promise<import('../llms/types').LLMsDataResponse | null>\n /** Use Signal AI visibility scores to compute page priority (40% visibility + 30% depth + 30% base) */\n intelligentPriority?: boolean\n}\n\n// Route groups and special folders to ignore\nconst IGNORED_FOLDERS = [\n 'api',\n 'admin',\n '_uptrade',\n '%5Fuptrade', // URL-encoded _uptrade\n 'sonor-setup',\n 'offline',\n]\n\n// File patterns that indicate a page\nconst PAGE_FILES = ['page.tsx', 'page.jsx', 'page.js', 'page.ts']\n\n/**\n * Strip trailing slash from a path (preserving '/' root).\n * Used to normalize paths before comparison so that '/about' and '/about/' are treated the same.\n */\nfunction normalizePath(path: string): string {\n if (path !== '/' && path.endsWith('/')) {\n return path.slice(0, -1)\n }\n return path\n}\n\n/**\n * Check if a path matches any exclude pattern.\n * Normalizes both the path and pattern prefixes to handle trailing slash inconsistencies.\n */\nfunction isExcluded(path: string, patterns: string[]): boolean {\n const normalized = normalizePath(path)\n return patterns.some(pattern => {\n if (pattern.endsWith('/*')) {\n const prefix = normalizePath(pattern.slice(0, -2))\n return normalized === prefix || normalized.startsWith(prefix + '/')\n }\n if (pattern.endsWith('*')) {\n const prefix = pattern.slice(0, -1)\n return normalized.startsWith(prefix)\n }\n return normalized === normalizePath(pattern)\n })\n}\n\n/**\n * Infer page type from URL path pattern.\n * Used to give Signal context about what each page covers.\n */\nfunction inferPageType(urlPath: string): PageType {\n if (urlPath === '/') return 'homepage'\n if (/^\\/services(\\/|$)/i.test(urlPath)) return 'service'\n if (/^\\/compare(\\/|$)/i.test(urlPath)) return 'comparison'\n if (/^\\/(insights|blog)(\\/|$)/i.test(urlPath)) return 'blog'\n if (/^\\/industries(\\/|$)/i.test(urlPath)) return 'industry'\n if (/^\\/work(\\/|$)/i.test(urlPath)) return 'portfolio'\n if (/^\\/faq(\\/|$)/i.test(urlPath)) return 'faq'\n if (/^\\/pricing(\\/|$)/i.test(urlPath)) return 'pricing'\n return 'page'\n}\n\n/**\n * Convert a file-system path (relative to appDir) into a URL path,\n * stripping route groups and cleaning slashes.\n */\nfunction toUrlPath(fsPath: string): string {\n let urlPath = '/' + fsPath\n // Handle route groups (parentheses) - remove them from URL\n urlPath = urlPath.replace(/\\/\\([^)]+\\)/g, '')\n // Clean up path\n urlPath = urlPath.replace(/\\/+/g, '/') // Remove double slashes\n if (urlPath !== '/' && urlPath.endsWith('/')) {\n urlPath = urlPath.slice(0, -1) // Remove trailing slash\n }\n return urlPath\n}\n\n/**\n * Attempt to call generateStaticParams from a dynamic route page module.\n * Returns the param objects, or null if unavailable.\n */\nasync function tryGenerateStaticParams(\n pageFilePath: string\n): Promise<Array<Record<string, string>> | null> {\n try {\n const fileUrl = pathToFileURL(pageFilePath).href\n // Use a timeout to guard against slow external API calls\n const importPromise = import(fileUrl)\n const timeoutPromise = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('generateStaticParams import timed out (5s)')), 5000)\n )\n const mod = await Promise.race([importPromise, timeoutPromise])\n\n if (typeof mod.generateStaticParams === 'function') {\n const resultPromise = Promise.resolve(mod.generateStaticParams())\n const resultTimeout = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('generateStaticParams call timed out (10s)')), 10000)\n )\n const params = await Promise.race([resultPromise, resultTimeout])\n if (Array.isArray(params)) {\n return params as Array<Record<string, string>>\n }\n }\n } catch {\n // Expected on Next.js 16+ where dynamic import() of page files fails\n // with \"Cannot find module as expression is too dynamic\".\n // This is harmless — dynamic routes are resolved via additionalPaths or dynamicRoutes config.\n }\n return null\n}\n\ninterface DiscoverPagesOptions {\n /** Explicit param values for dynamic segments, keyed by segment name e.g. '[slug]' */\n dynamicRoutes?: Record<string, string[]>\n /** Whether to attempt auto-import of generateStaticParams (default true) */\n resolveGenerateStaticParams?: boolean\n}\n\n/**\n * Recursively discover pages from the app directory.\n * Handles dynamic route segments via explicit config or generateStaticParams auto-discovery.\n */\nasync function discoverPages(\n appDir: string,\n currentPath: string = '',\n pages: string[] = [],\n options: DiscoverPagesOptions = {}\n): Promise<string[]> {\n const fullPath = join(appDir, currentPath)\n\n if (!existsSync(fullPath)) {\n return pages\n }\n\n const entries = readdirSync(fullPath)\n\n // Check if this directory has a page file\n const hasPage = entries.some(entry => PAGE_FILES.includes(entry))\n\n if (hasPage) {\n const urlPath = toUrlPath(currentPath)\n // Only add if the path has no unresolved dynamic segments\n if (!urlPath.includes('[')) {\n pages.push(urlPath)\n }\n }\n\n // Recurse into subdirectories\n for (const entry of entries) {\n const entryPath = join(fullPath, entry)\n const stat = statSync(entryPath)\n\n if (stat.isDirectory()) {\n // Skip ignored folders\n const folderName = entry.toLowerCase()\n if (IGNORED_FOLDERS.some(ignored => folderName.includes(ignored))) {\n continue\n }\n\n // Skip private folders\n if (entry.startsWith('_')) {\n continue\n }\n\n // Handle dynamic route segments\n if (entry.startsWith('[') && entry.endsWith(']')) {\n const paramName = entry.slice(1, -1) // e.g. 'slug' from '[slug]'\n let paramValues: string[] | null = null\n\n // 1. Check explicit dynamicRoutes config first\n if (options.dynamicRoutes) {\n // Match by full segment name '[slug]' or bare param name 'slug'\n paramValues = options.dynamicRoutes[entry] ?? options.dynamicRoutes[paramName] ?? null\n }\n\n // 2. Fall back to auto-importing generateStaticParams\n if (!paramValues && options.resolveGenerateStaticParams !== false) {\n // Find the page file inside the dynamic directory\n const dynamicDirPath = join(fullPath, entry)\n const pageFile = PAGE_FILES.find(pf => existsSync(join(dynamicDirPath, pf)))\n\n if (pageFile) {\n const pageFilePath = resolve(dynamicDirPath, pageFile)\n const params = await tryGenerateStaticParams(pageFilePath)\n if (params && params.length > 0) {\n // Extract the values for this specific param\n paramValues = params\n .map(p => p[paramName])\n .filter((v): v is string => typeof v === 'string')\n }\n }\n }\n\n if (paramValues && paramValues.length > 0) {\n const dynamicDirPath = join(fullPath, entry)\n const dynamicEntries = readdirSync(dynamicDirPath)\n\n // Check if the dynamic directory itself has a page file\n const dynamicHasPage = dynamicEntries.some(e => PAGE_FILES.includes(e))\n\n for (const value of paramValues) {\n // If the dynamic dir has a page, add the resolved path\n if (dynamicHasPage) {\n const resolvedPath = toUrlPath(join(currentPath, value))\n if (!resolvedPath.includes('[')) {\n pages.push(resolvedPath)\n }\n }\n\n // Recurse into subdirectories inside the dynamic segment,\n // mapping [param] on disk to the resolved value in the URL\n for (const subEntry of dynamicEntries) {\n if (PAGE_FILES.includes(subEntry)) continue // skip page files\n const subEntryPath = join(dynamicDirPath, subEntry)\n if (statSync(subEntryPath).isDirectory()) {\n // For nested dynamic segments or regular subdirs, recurse\n // We need a custom appDir-relative path that uses the resolved value\n // but reads from the dynamic dir on disk\n await discoverPagesFromDisk(\n dynamicDirPath,\n subEntry,\n join(currentPath, value),\n pages,\n options\n )\n }\n }\n }\n } else {\n // Only log at debug level — dynamic segments are typically handled\n // by additionalPaths (blog posts, portfolio items, etc.)\n console.debug(\n `[site-kit] Dynamic segment \"${entry}\" at /${currentPath} not resolved via filesystem. ` +\n `If these routes are missing from sitemap, add them via additionalPaths or dynamicRoutes config.`\n )\n }\n continue\n }\n\n await discoverPages(appDir, join(currentPath, entry), pages, options)\n }\n }\n\n return pages\n}\n\n/**\n * Discover pages starting from a disk path that differs from the URL path.\n * Used when traversing inside dynamic route segments where the filesystem\n * directory name (e.g. [slug]) differs from the URL segment (e.g. 'seo').\n */\nasync function discoverPagesFromDisk(\n diskParent: string,\n diskEntry: string,\n urlParentPath: string,\n pages: string[],\n options: DiscoverPagesOptions\n): Promise<void> {\n const diskPath = join(diskParent, diskEntry)\n if (!existsSync(diskPath) || !statSync(diskPath).isDirectory()) return\n\n const entries = readdirSync(diskPath)\n const hasPage = entries.some(e => PAGE_FILES.includes(e))\n const urlPath = toUrlPath(join(urlParentPath, diskEntry))\n\n if (hasPage && !urlPath.includes('[')) {\n pages.push(urlPath)\n }\n\n for (const sub of entries) {\n if (PAGE_FILES.includes(sub)) continue\n const subPath = join(diskPath, sub)\n if (!statSync(subPath).isDirectory()) continue\n\n const folderName = sub.toLowerCase()\n if (IGNORED_FOLDERS.some(ignored => folderName.includes(ignored))) continue\n if (sub.startsWith('_')) continue\n\n if (sub.startsWith('[') && sub.endsWith(']')) {\n // Nested dynamic segment — resolve its params too\n const nestedParam = sub.slice(1, -1)\n let nestedValues: string[] | null = null\n\n if (options.dynamicRoutes) {\n nestedValues = options.dynamicRoutes[sub] ?? options.dynamicRoutes[nestedParam] ?? null\n }\n\n if (!nestedValues && options.resolveGenerateStaticParams !== false) {\n const nestedDir = join(diskPath, sub)\n const pageFile = PAGE_FILES.find(pf => existsSync(join(nestedDir, pf)))\n if (pageFile) {\n const params = await tryGenerateStaticParams(resolve(nestedDir, pageFile))\n if (params && params.length > 0) {\n nestedValues = params.map(p => p[nestedParam]).filter((v): v is string => typeof v === 'string')\n }\n }\n }\n\n if (nestedValues && nestedValues.length > 0) {\n const nestedDir = join(diskPath, sub)\n const nestedEntries = readdirSync(nestedDir)\n const nestedHasPage = nestedEntries.some(e => PAGE_FILES.includes(e))\n for (const val of nestedValues) {\n if (nestedHasPage) {\n const resolved = toUrlPath(join(urlParentPath, diskEntry, val))\n if (!resolved.includes('[')) pages.push(resolved)\n }\n for (const nestedSub of nestedEntries) {\n if (PAGE_FILES.includes(nestedSub)) continue\n const nestedSubPath = join(nestedDir, nestedSub)\n if (statSync(nestedSubPath).isDirectory()) {\n await discoverPagesFromDisk(nestedDir, nestedSub, join(urlParentPath, diskEntry, val), pages, options)\n }\n }\n }\n }\n continue\n }\n\n await discoverPagesFromDisk(diskPath, sub, join(urlParentPath, diskEntry), pages, options)\n }\n}\n\n/**\n * Find the app directory from the current working directory\n */\nfunction findAppDir(): string {\n const cwd = process.cwd()\n \n // Try common locations\n const candidates = [\n join(cwd, 'app'),\n join(cwd, 'src', 'app'),\n ]\n \n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate\n }\n }\n \n throw new Error(\n '[site-kit] Could not find app directory. Ensure you have an \"app\" or \"src/app\" folder.'\n )\n}\n\n/**\n * Get priority for a path based on config\n */\nfunction getPriority(path: string, config: SitemapConfig): number {\n const normalized = normalizePath(path)\n // Check priority overrides\n if (config.priorities) {\n for (const [pattern, priority] of Object.entries(config.priorities)) {\n if (pattern.endsWith('/*')) {\n const prefix = normalizePath(pattern.slice(0, -2))\n if (normalized === prefix || normalized.startsWith(prefix + '/')) return priority\n } else if (normalized === normalizePath(pattern)) {\n return priority\n }\n }\n }\n \n // Home page gets highest priority\n if (path === '/') return 1.0\n \n // Shallow pages get higher priority\n const depth = path.split('/').filter(Boolean).length\n if (depth === 1) return 0.8\n if (depth === 2) return 0.6\n \n return config.defaultPriority ?? 0.5\n}\n\nexport interface SyncSitemapOptions {\n /** Trigger Signal AI meta optimization after sync */\n optimizeMeta?: boolean\n /** When true, wait for meta optimization to complete (for build-time llms.txt) */\n awaitMeta?: boolean\n}\n\n/**\n * Sync sitemap entries to Portal API\n * Called automatically by createSitemap at build time\n */\nexport async function syncSitemapToPortal(\n entries: SitemapEntry[],\n apiUrl: string,\n apiKey: string,\n options?: SyncSitemapOptions\n): Promise<{ success: boolean; created: number; updated: number }> {\n if (!apiKey) {\n console.warn('[site-kit] No API key provided, skipping sitemap sync')\n return { success: false, created: 0, updated: 0 }\n }\n \n const normalizedEntries = entries.map(entry => {\n let path: string\n try {\n const url = new URL(entry.url)\n path = url.pathname\n } catch {\n path = entry.url.startsWith('/') ? entry.url : `/${entry.url}`\n }\n\n // Strip embedded domains from malformed paths (e.g. \"//domain.com/path\")\n path = path.replace(/^\\/{1,2}[a-z0-9.-]+\\.[a-z]{2,}(\\/|$)/i, '/')\n\n const normalized: Record<string, unknown> = {\n path,\n priority: entry.priority,\n changefreq: entry.changeFrequency,\n pageType: entry.pageType ?? inferPageType(path),\n }\n if (entry.description) {\n normalized.description = entry.description\n }\n return normalized\n }).filter(entry => {\n const p = entry.path as string\n // Reject paths that still contain embedded URLs or domains\n if (p.includes('://') || /\\/\\/[a-z0-9.-]+\\.[a-z]{2,}/i.test(p)) {\n console.warn(`[site-kit] Filtering malformed sitemap path: ${p}`)\n return false\n }\n return true\n })\n\n try {\n const body: Record<string, unknown> = { entries: normalizedEntries, mode: 'full-replace' }\n if (options?.optimizeMeta !== undefined) body.optimize_meta = options.optimizeMeta\n if (options?.awaitMeta !== undefined) body.await_meta = options.awaitMeta\n\n const response = await fetch(`${apiUrl}/api/public/seo/register-sitemap`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify(body),\n })\n \n if (!response.ok) {\n console.error('[site-kit] Sitemap sync failed:', response.status, await response.text())\n return { success: false, created: 0, updated: 0 }\n }\n \n const result = await response.json()\n \n return {\n success: true,\n created: result.created || 0,\n updated: result.updated || 0,\n }\n } catch (error) {\n console.error('[site-kit] Sitemap sync error:', error)\n return { success: false, created: 0, updated: 0 }\n }\n}\n\n/**\n * Create a sitemap generator function for Next.js\n * Automatically syncs to Portal API at build time.\n * \n * @example\n * ```ts\n * // app/sitemap.ts\n * import { createSitemap } from '@sonordev/site-kit/sitemap'\n * \n * export default createSitemap({\n * baseUrl: 'https://example.com'\n * })\n * ```\n */\n/**\n * Fetches published portfolio items from Portal API and returns them as sitemap paths.\n * Use this in createSitemap's additionalPaths to include portfolio routes.\n *\n * @example\n * ```ts\n * import { createSitemap, getPortfolioPaths } from '@sonordev/site-kit/sitemap'\n *\n * export default createSitemap({\n * baseUrl: 'https://example.com',\n * additionalPaths: async () => [\n * ...(await getPortfolioPaths()),\n * ]\n * })\n * ```\n */\nexport async function getPortfolioPaths(options?: {\n apiUrl?: string\n apiKey?: string\n basePath?: string // defaults to '/portfolio'\n priority?: number // defaults to 0.7\n}): Promise<Array<{ path: string; priority: number; changeFrequency: 'weekly' }>> {\n const apiUrl = options?.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n const apiKey = options?.apiKey || process.env.SONOR_API_KEY || process.env.UPTRADE_API_KEY || ''\n const basePath = options?.basePath || '/work'\n const priority = options?.priority || 0.7\n\n if (!apiKey) return []\n\n try {\n const response = await fetch(`${apiUrl}/api/public/portfolio/items`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n })\n\n if (!response.ok) return []\n\n const data = await response.json()\n const items = data?.portfolioItems || data?.items || data || []\n\n return items\n .filter((item: any) => item.slug && item.status === 'published')\n .map((item: any) => ({\n path: `${basePath}/${item.slug}`,\n priority,\n changeFrequency: 'weekly' as const,\n }))\n } catch {\n console.warn('[site-kit] Failed to fetch portfolio items for sitemap')\n return []\n }\n}\n\nexport function createSitemap(config: SitemapConfig): () => SitemapEntry[] | Promise<SitemapEntry[]> {\n return async () => {\n const { exclude = [], defaultChangeFrequency = 'weekly' } = config\n \n // Resolve baseUrl: from config, or from Portal via apiKey/env\n let baseUrl = config.baseUrl\n if (!baseUrl) {\n const apiKey = config.apiKey ?? process.env.SONOR_API_KEY\n const site = await getSiteConfig({ apiKey, apiUrl: config.apiUrl })\n baseUrl = site?.site_url ?? process.env.NEXT_PUBLIC_SITE_URL ?? 'https://example.com'\n }\n const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl\n \n // Default exclusions\n const allExclusions = [\n '/api/*',\n '/admin/*',\n '/_uptrade/*',\n '/sonor-setup/*',\n '/offline/*',\n ...exclude,\n ]\n \n // Discover pages from app directory (including dynamic routes)\n let pages: string[] = []\n let discoveryFailed = false\n try {\n const appDir = findAppDir()\n pages = await discoverPages(appDir, '', [], {\n dynamicRoutes: config.dynamicRoutes,\n resolveGenerateStaticParams: config.resolveGenerateStaticParams,\n })\n } catch (error) {\n console.warn('[site-kit] Failed to discover pages from filesystem:', (error as Error).message)\n // Filesystem discovery failed (typical during ISR revalidation on Netlify/Vercel\n // where the app/ directory doesn't exist in the standalone serverless bundle).\n // Fetch the page list from Portal API instead — it has the correct data from\n // the last build-time full-replace sync.\n discoveryFailed = true\n const apiKey = config.apiKey || process.env.SONOR_API_KEY || process.env.UPTRADE_API_KEY || ''\n const apiUrl = config.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n if (apiKey && normalizedBaseUrl) {\n try {\n const domain = normalizedBaseUrl.replace(/^https?:\\/\\/(www\\.)?/, '')\n const pagesResponse = await fetch(\n `${apiUrl}/api/public/seo/metadata?domain=${encodeURIComponent(domain)}`,\n { headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey } }\n )\n if (pagesResponse.ok) {\n const pagesData = await pagesResponse.json()\n const pagesList: Array<{ path: string }> = pagesData?.pagesList || []\n pages = pagesList.map(p => p.path).filter(Boolean)\n console.log(`[site-kit] Recovered ${pages.length} pages from Portal API (ISR fallback)`)\n }\n } catch (fetchErr) {\n console.warn('[site-kit] Portal API fallback failed:', (fetchErr as Error).message)\n }\n }\n if (pages.length === 0) {\n pages = ['/']\n }\n }\n \n // Normalize, deduplicate, and filter out excluded pages\n pages = Array.from(new Set(pages.map(normalizePath))).filter(page => !isExcluded(page, allExclusions))\n\n // Build sitemap entries\n const entries: SitemapEntry[] = pages.map(path => ({\n url: `${normalizedBaseUrl}${path}`,\n lastModified: new Date(),\n changeFrequency: defaultChangeFrequency,\n priority: getPriority(path, config),\n pageType: inferPageType(path),\n }))\n\n // Add additional dynamic paths if provided (deduplicating against discovered pages)\n if (config.additionalPaths) {\n try {\n // Normalize URLs for dedup so '/about' and '/about/' are treated the same\n const existingPaths = new Set(pages)\n const additional = await config.additionalPaths()\n for (const item of additional) {\n const normalizedItemPath = normalizePath(item.path)\n if (!isExcluded(normalizedItemPath, allExclusions) && !existingPaths.has(normalizedItemPath)) {\n entries.push({\n url: `${normalizedBaseUrl}${normalizedItemPath}`,\n lastModified: new Date(),\n changeFrequency: item.changeFrequency ?? defaultChangeFrequency,\n priority: item.priority ?? getPriority(normalizedItemPath, config),\n pageType: inferPageType(normalizedItemPath),\n ...(item.description ? { description: item.description } : {}),\n })\n existingPaths.add(normalizedItemPath)\n }\n }\n } catch (error) {\n console.warn('[site-kit] Failed to get additional paths:', error)\n }\n }\n \n // Intelligent priority: blend visibility scores with depth-based priority\n if (config.intelligentPriority) {\n try {\n const apiKey = config.apiKey ?? process.env.SONOR_API_KEY ?? ''\n const projectIdMatch = apiKey.match(/^(?:sonor|uptrade)_([0-9a-f-]{36})_/)\n const projectId = projectIdMatch ? projectIdMatch[1] : null\n\n if (projectId) {\n const signalApiUrl = process.env.SONOR_SIGNAL_URL || process.env.SIGNAL_API_URL || process.env.NEXT_PUBLIC_SIGNAL_API_URL || 'https://signal.sonor.io'\n const visResponse = await fetch(`${signalApiUrl}/skills/seo/visibility/${projectId}`, {\n headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },\n }).catch(() => null)\n\n if (visResponse?.ok) {\n const visData = await visResponse.json()\n const scores = (visData?.data || visData) as Array<{ page_path: string; overall_score: number }>\n const scoreMap = new Map(scores.map(s => [s.page_path, s.overall_score]))\n\n let appliedCount = 0\n for (const entry of entries) {\n const path = new URL(entry.url).pathname\n const visScore = scoreMap.get(path)\n if (visScore !== undefined) {\n const depthBonus = path === '/' ? 1.0 : Math.max(0.3, 1.0 - (path.split('/').filter(Boolean).length * 0.15))\n // 40% visibility + 30% depth + 30% base\n entry.priority = Math.round((visScore * 0.4 + depthBonus * 0.3 + (entry.priority ?? 0.5) * 0.3) * 100) / 100\n appliedCount++\n }\n }\n if (appliedCount > 0) {\n console.log(`[site-kit] Applied intelligent priority from ${appliedCount} visibility scores`)\n }\n }\n }\n } catch {\n // Signal API unavailable — fall back to static priority\n }\n }\n\n // Sort by priority descending\n entries.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))\n\n console.log(`[site-kit] Generated sitemap with ${entries.length} pages`)\n \n // Auto-sync to Portal API at build time only (skip in dev — sitemap.xml is re-rendered on every request)\n // Also skip sync when page discovery failed (e.g. ISR revalidation where the app/ directory\n // doesn't exist in the standalone serverless bundle). Running full-replace with only\n // additionalPaths would DELETE all properly-synced static routes discovered at build time.\n const isDev = process.env.NODE_ENV === 'development'\n if (!config.disableSync && !isDev && !discoveryFailed) {\n const apiUrl = config.apiUrl || process.env.SONOR_API_URL || 'https://api.sonor.io'\n const apiKey = config.apiKey || process.env.SONOR_API_KEY\n \n if (apiKey) {\n try {\n const syncOptions: SyncSitemapOptions = {}\n if (config.awaitMetaOptimization === true) {\n syncOptions.optimizeMeta = true\n syncOptions.awaitMeta = true\n }\n const result = await syncSitemapToPortal(entries, apiUrl, apiKey, syncOptions)\n if (result.success) {\n console.log(`[site-kit] Synced to Portal API: ${result.created} created, ${result.updated} updated`)\n\n // Verification: report meta optimization coverage after sync\n if (config.awaitMetaOptimization === true) {\n try {\n const domain = normalizedBaseUrl.replace(/^https?:\\/\\/(www\\.)?/, '')\n const pagesResponse = await fetch(\n `${apiUrl}/api/public/seo/metadata?domain=${encodeURIComponent(domain)}`,\n { headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey } }\n )\n if (pagesResponse.ok) {\n const pagesData = await pagesResponse.json()\n const allPages: Array<{ managed_title?: string; managed_meta_description?: string }> = pagesData?.pagesList || []\n const optimized = allPages.filter(p => p.managed_title && p.managed_meta_description)\n console.log(`[site-kit] Meta optimization coverage: ${optimized.length}/${allPages.length} pages (${allPages.length ? Math.round((optimized.length / allPages.length) * 100) : 0}%)`)\n }\n } catch {\n console.warn('[site-kit] Could not verify meta optimization coverage')\n }\n }\n }\n\n // Write AI-optimized llms.txt after sync (default true when API key present)\n if (config.optimizedLLMsTxt !== false) {\n try {\n const writeResult = await writeLLMsTxtToPublic({\n apiUrl,\n apiKey,\n full: config.optimizedLLMsFullTxt ?? false,\n fallbackToLocal: true,\n getLocalData: config.getLocalData,\n })\n if (writeResult.success) {\n console.log(`[site-kit] Wrote llms.txt (optimized=${writeResult.optimized})`)\n }\n } catch (err) {\n console.warn('[site-kit] Failed to write llms.txt:', (err as Error).message)\n }\n }\n } catch (err) {\n console.warn('[site-kit] Failed to sync sitemap to Portal API:', (err as Error).message)\n }\n } else {\n console.log('[site-kit] No API key found, skipping Portal API sync')\n }\n }\n \n return entries\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunkUHMM22HX_js = require('./chunk-UHMM22HX.js');
|
|
4
|
-
require('./chunk-ZSMWDLMK.js');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Object.defineProperty(exports, "SitemapSync", {
|
|
9
|
-
enumerable: true,
|
|
10
|
-
get: function () { return chunkUHMM22HX_js.SitemapSync; }
|
|
11
|
-
});
|
|
12
|
-
//# sourceMappingURL=SitemapSync-TPA3GTGU.js.map
|
|
13
|
-
//# sourceMappingURL=SitemapSync-TPA3GTGU.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/SitemapSync.tsx"],"names":[],"mappings":"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","file":"chunk-PU5ULVL5.mjs","sourcesContent":["/**\n * SitemapSync - Automatically sync sitemap.xml to Portal API\n *\n * Client-only component. Lives at package root so the main entry does not\n * pull in seo/ (and server-only code). For use in SiteKitProvider and\n * from @sonordev/site-kit/seo.\n *\n * @example\n * ```tsx\n * import { SitemapSync } from '@sonordev/site-kit'\n * // or\n * import { SitemapSync } from '@sonordev/site-kit/seo'\n * ```\n */\n\n'use client'\n\nimport { useEffect, useRef } from 'react'\n\ninterface SitemapSyncProps {\n /** Custom sitemap URL (defaults to /sitemap.xml) */\n sitemapUrl?: string\n /** How often to re-sync in minutes (0 = only on mount, default: 60) */\n syncInterval?: number\n /** Enable debug logging */\n debug?: boolean\n /**\n * Sync mode:\n * - 'additive' (default for runtime): adds/updates pages but never deletes existing ones\n * - 'full-replace': replaces the entire page set (use only for build-time sync via createSitemap)\n */\n mode?: 'additive' | 'full-replace'\n}\n\ninterface SitemapEntry {\n path: string\n lastmod?: string\n changefreq?: string\n priority?: number\n pageType?: string\n}\n\n/**\n * Infer page type from URL path pattern for Signal context.\n */\nfunction inferPageType(urlPath: string): string {\n if (urlPath === '/' || urlPath === '') return 'homepage'\n if (/^\\/services(\\/|$)/i.test(urlPath)) return 'service'\n if (/^\\/compare(\\/|$)/i.test(urlPath)) return 'comparison'\n if (/^\\/(insights|blog)(\\/|$)/i.test(urlPath)) return 'blog'\n if (/^\\/industries(\\/|$)/i.test(urlPath)) return 'industry'\n if (/^\\/work(\\/|$)/i.test(urlPath)) return 'portfolio'\n if (/^\\/faq(\\/|$)/i.test(urlPath)) return 'faq'\n if (/^\\/pricing(\\/|$)/i.test(urlPath)) return 'pricing'\n return 'page'\n}\n\nfunction getApiConfig() {\n if (typeof window === 'undefined') return { apiUrl: '', apiKey: '' }\n\n const apiUrl = (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n const apiKey = (window as any).__SITE_KIT_API_KEY__ || ''\n return { apiUrl, apiKey }\n}\n\nasync function parseSitemap(sitemapUrl: string, debug: boolean): Promise<SitemapEntry[]> {\n try {\n const response = await fetch(sitemapUrl)\n if (!response.ok) {\n if (debug) console.warn('[SitemapSync] Failed to fetch sitemap:', response.status)\n return []\n }\n\n const text = await response.text()\n const parser = new DOMParser()\n const doc = parser.parseFromString(text, 'application/xml')\n\n const parseError = doc.querySelector('parsererror')\n if (parseError) {\n if (debug) console.warn('[SitemapSync] Sitemap parse error:', parseError.textContent)\n return []\n }\n\n const urls = doc.querySelectorAll('url')\n const entries: SitemapEntry[] = []\n\n urls.forEach(url => {\n const loc = url.querySelector('loc')?.textContent\n if (!loc) return\n\n let path: string\n try {\n const urlObj = new URL(loc)\n path = urlObj.pathname\n } catch {\n path = loc.startsWith('/') ? loc : `/${loc}`\n }\n\n if (path !== '/' && path.endsWith('/')) {\n path = path.slice(0, -1)\n }\n\n const lastmod = url.querySelector('lastmod')?.textContent || undefined\n const changefreq = url.querySelector('changefreq')?.textContent || undefined\n const priorityStr = url.querySelector('priority')?.textContent\n const priority = priorityStr ? parseFloat(priorityStr) : undefined\n\n entries.push({ path, lastmod, changefreq, priority })\n })\n\n if (debug) {\n console.log(`[SitemapSync] Parsed ${entries.length} entries from sitemap`)\n }\n\n return entries\n } catch (error) {\n if (debug) console.error('[SitemapSync] Error parsing sitemap:', error)\n return []\n }\n}\n\nasync function syncToPortal(\n entries: SitemapEntry[],\n apiUrl: string,\n apiKey: string,\n debug: boolean,\n mode: 'additive' | 'full-replace' = 'additive'\n): Promise<{ success: boolean; created: number; updated: number }> {\n if (!apiKey) {\n if (debug) console.warn('[SitemapSync] No API key configured')\n return { success: false, created: 0, updated: 0 }\n }\n\n if (entries.length === 0) {\n if (debug) console.log('[SitemapSync] No entries to sync')\n return { success: true, created: 0, updated: 0 }\n }\n\n try {\n const response = await fetch(`${apiUrl}/api/public/seo/register-sitemap`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify({\n entries: entries.map(e => ({\n path: e.path,\n priority: e.priority,\n changefreq: e.changefreq,\n pageType: inferPageType(e.path),\n })),\n mode,\n }),\n })\n\n if (!response.ok) {\n if (debug) console.error('[SitemapSync] API error:', response.status, response.statusText)\n return { success: false, created: 0, updated: 0 }\n }\n\n const result = await response.json()\n\n if (debug) {\n console.log(`[SitemapSync] Synced: ${result.created} created, ${result.updated} updated`)\n }\n\n return {\n success: true,\n created: result.created || 0,\n updated: result.updated || 0,\n }\n } catch (error) {\n if (debug) console.error('[SitemapSync] Sync error:', error)\n return { success: false, created: 0, updated: 0 }\n }\n}\n\nexport function SitemapSync({\n sitemapUrl = '/sitemap.xml',\n syncInterval = 60,\n debug = false,\n mode = 'additive',\n}: SitemapSyncProps) {\n const hasSyncedRef = useRef(false)\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\n\n useEffect(() => {\n // Build-time sync via createSitemap is the canonical source of truth, but\n // it can fail during ISR revalidation (app/ dir unavailable on serverless).\n // Runtime SitemapSync in additive mode is a safe fallback — it adds/updates\n // pages that may be missing without ever deleting existing ones.\n // Full-replace is never safe at runtime — always force additive.\n const effectiveMode: 'additive' | 'full-replace' = 'additive'\n\n const doSync = async () => {\n const { apiUrl, apiKey } = getApiConfig()\n\n if (!apiKey) {\n if (debug) console.warn('[SitemapSync] No API key found')\n return\n }\n\n if (debug) console.log('[SitemapSync] Starting sitemap sync...')\n\n const entries = await parseSitemap(sitemapUrl, debug)\n\n if (entries.length > 0) {\n await syncToPortal(entries, apiUrl, apiKey, debug, effectiveMode)\n }\n }\n\n if (!hasSyncedRef.current) {\n hasSyncedRef.current = true\n doSync()\n }\n\n if (syncInterval > 0) {\n intervalRef.current = setInterval(doSync, syncInterval * 60 * 1000)\n }\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current)\n }\n }\n }, [sitemapUrl, syncInterval, debug, mode])\n\n return null\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/SitemapSync.tsx"],"names":["useRef","useEffect"],"mappings":"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","file":"chunk-UHMM22HX.js","sourcesContent":["/**\n * SitemapSync - Automatically sync sitemap.xml to Portal API\n *\n * Client-only component. Lives at package root so the main entry does not\n * pull in seo/ (and server-only code). For use in SiteKitProvider and\n * from @sonordev/site-kit/seo.\n *\n * @example\n * ```tsx\n * import { SitemapSync } from '@sonordev/site-kit'\n * // or\n * import { SitemapSync } from '@sonordev/site-kit/seo'\n * ```\n */\n\n'use client'\n\nimport { useEffect, useRef } from 'react'\n\ninterface SitemapSyncProps {\n /** Custom sitemap URL (defaults to /sitemap.xml) */\n sitemapUrl?: string\n /** How often to re-sync in minutes (0 = only on mount, default: 60) */\n syncInterval?: number\n /** Enable debug logging */\n debug?: boolean\n /**\n * Sync mode:\n * - 'additive' (default for runtime): adds/updates pages but never deletes existing ones\n * - 'full-replace': replaces the entire page set (use only for build-time sync via createSitemap)\n */\n mode?: 'additive' | 'full-replace'\n}\n\ninterface SitemapEntry {\n path: string\n lastmod?: string\n changefreq?: string\n priority?: number\n pageType?: string\n}\n\n/**\n * Infer page type from URL path pattern for Signal context.\n */\nfunction inferPageType(urlPath: string): string {\n if (urlPath === '/' || urlPath === '') return 'homepage'\n if (/^\\/services(\\/|$)/i.test(urlPath)) return 'service'\n if (/^\\/compare(\\/|$)/i.test(urlPath)) return 'comparison'\n if (/^\\/(insights|blog)(\\/|$)/i.test(urlPath)) return 'blog'\n if (/^\\/industries(\\/|$)/i.test(urlPath)) return 'industry'\n if (/^\\/work(\\/|$)/i.test(urlPath)) return 'portfolio'\n if (/^\\/faq(\\/|$)/i.test(urlPath)) return 'faq'\n if (/^\\/pricing(\\/|$)/i.test(urlPath)) return 'pricing'\n return 'page'\n}\n\nfunction getApiConfig() {\n if (typeof window === 'undefined') return { apiUrl: '', apiKey: '' }\n\n const apiUrl = (window as any).__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n const apiKey = (window as any).__SITE_KIT_API_KEY__ || ''\n return { apiUrl, apiKey }\n}\n\nasync function parseSitemap(sitemapUrl: string, debug: boolean): Promise<SitemapEntry[]> {\n try {\n const response = await fetch(sitemapUrl)\n if (!response.ok) {\n if (debug) console.warn('[SitemapSync] Failed to fetch sitemap:', response.status)\n return []\n }\n\n const text = await response.text()\n const parser = new DOMParser()\n const doc = parser.parseFromString(text, 'application/xml')\n\n const parseError = doc.querySelector('parsererror')\n if (parseError) {\n if (debug) console.warn('[SitemapSync] Sitemap parse error:', parseError.textContent)\n return []\n }\n\n const urls = doc.querySelectorAll('url')\n const entries: SitemapEntry[] = []\n\n urls.forEach(url => {\n const loc = url.querySelector('loc')?.textContent\n if (!loc) return\n\n let path: string\n try {\n const urlObj = new URL(loc)\n path = urlObj.pathname\n } catch {\n path = loc.startsWith('/') ? loc : `/${loc}`\n }\n\n if (path !== '/' && path.endsWith('/')) {\n path = path.slice(0, -1)\n }\n\n const lastmod = url.querySelector('lastmod')?.textContent || undefined\n const changefreq = url.querySelector('changefreq')?.textContent || undefined\n const priorityStr = url.querySelector('priority')?.textContent\n const priority = priorityStr ? parseFloat(priorityStr) : undefined\n\n entries.push({ path, lastmod, changefreq, priority })\n })\n\n if (debug) {\n console.log(`[SitemapSync] Parsed ${entries.length} entries from sitemap`)\n }\n\n return entries\n } catch (error) {\n if (debug) console.error('[SitemapSync] Error parsing sitemap:', error)\n return []\n }\n}\n\nasync function syncToPortal(\n entries: SitemapEntry[],\n apiUrl: string,\n apiKey: string,\n debug: boolean,\n mode: 'additive' | 'full-replace' = 'additive'\n): Promise<{ success: boolean; created: number; updated: number }> {\n if (!apiKey) {\n if (debug) console.warn('[SitemapSync] No API key configured')\n return { success: false, created: 0, updated: 0 }\n }\n\n if (entries.length === 0) {\n if (debug) console.log('[SitemapSync] No entries to sync')\n return { success: true, created: 0, updated: 0 }\n }\n\n try {\n const response = await fetch(`${apiUrl}/api/public/seo/register-sitemap`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify({\n entries: entries.map(e => ({\n path: e.path,\n priority: e.priority,\n changefreq: e.changefreq,\n pageType: inferPageType(e.path),\n })),\n mode,\n }),\n })\n\n if (!response.ok) {\n if (debug) console.error('[SitemapSync] API error:', response.status, response.statusText)\n return { success: false, created: 0, updated: 0 }\n }\n\n const result = await response.json()\n\n if (debug) {\n console.log(`[SitemapSync] Synced: ${result.created} created, ${result.updated} updated`)\n }\n\n return {\n success: true,\n created: result.created || 0,\n updated: result.updated || 0,\n }\n } catch (error) {\n if (debug) console.error('[SitemapSync] Sync error:', error)\n return { success: false, created: 0, updated: 0 }\n }\n}\n\nexport function SitemapSync({\n sitemapUrl = '/sitemap.xml',\n syncInterval = 60,\n debug = false,\n mode = 'additive',\n}: SitemapSyncProps) {\n const hasSyncedRef = useRef(false)\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\n\n useEffect(() => {\n // Build-time sync via createSitemap is the canonical source of truth, but\n // it can fail during ISR revalidation (app/ dir unavailable on serverless).\n // Runtime SitemapSync in additive mode is a safe fallback — it adds/updates\n // pages that may be missing without ever deleting existing ones.\n // Full-replace is never safe at runtime — always force additive.\n const effectiveMode: 'additive' | 'full-replace' = 'additive'\n\n const doSync = async () => {\n const { apiUrl, apiKey } = getApiConfig()\n\n if (!apiKey) {\n if (debug) console.warn('[SitemapSync] No API key found')\n return\n }\n\n if (debug) console.log('[SitemapSync] Starting sitemap sync...')\n\n const entries = await parseSitemap(sitemapUrl, debug)\n\n if (entries.length > 0) {\n await syncToPortal(entries, apiUrl, apiKey, debug, effectiveMode)\n }\n }\n\n if (!hasSyncedRef.current) {\n hasSyncedRef.current = true\n doSync()\n }\n\n if (syncInterval > 0) {\n intervalRef.current = setInterval(doSync, syncInterval * 60 * 1000)\n }\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current)\n }\n }\n }, [sitemapUrl, syncInterval, debug, mode])\n\n return null\n}\n"]}
|