@uptrademedia/site-kit 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/api-CWtoFJCO.d.mts +137 -0
  2. package/dist/api-CWtoFJCO.d.ts +137 -0
  3. package/dist/{api-QUIPJJCX.js → api-UBHLAPUG.js} +20 -20
  4. package/dist/api-UBHLAPUG.js.map +1 -0
  5. package/dist/{api-V3BA5PMX.mjs → api-XNF6Q5HO.mjs} +3 -3
  6. package/dist/api-XNF6Q5HO.mjs.map +1 -0
  7. package/dist/blog/index.d.mts +131 -5
  8. package/dist/blog/index.d.ts +131 -5
  9. package/dist/blog/index.js +532 -302
  10. package/dist/blog/index.js.map +1 -1
  11. package/dist/blog/index.mjs +532 -302
  12. package/dist/blog/index.mjs.map +1 -1
  13. package/dist/{chunk-QQB4FO4Q.js → chunk-AWAJEIZS.js} +11 -8
  14. package/dist/chunk-AWAJEIZS.js.map +1 -0
  15. package/dist/{chunk-MB3WR5KJ.mjs → chunk-CDJL2YGL.mjs} +61 -443
  16. package/dist/chunk-CDJL2YGL.mjs.map +1 -0
  17. package/dist/{chunk-TDK7DLCH.js → chunk-FLAA4ZJO.js} +59 -448
  18. package/dist/chunk-FLAA4ZJO.js.map +1 -0
  19. package/dist/{chunk-JGQPAXTL.mjs → chunk-H5AGHERY.mjs} +8 -5
  20. package/dist/chunk-H5AGHERY.mjs.map +1 -0
  21. package/dist/{chunk-VDI7KYME.js → chunk-IYVJGUYX.js} +8 -4
  22. package/dist/chunk-IYVJGUYX.js.map +1 -0
  23. package/dist/{chunk-FQVGK746.mjs → chunk-SKHOW2CI.mjs} +8 -4
  24. package/dist/chunk-SKHOW2CI.mjs.map +1 -0
  25. package/dist/cli/index.js +32 -25
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/cli/index.mjs +32 -25
  28. package/dist/cli/index.mjs.map +1 -1
  29. package/dist/images/index.d.mts +49 -126
  30. package/dist/images/index.d.ts +49 -126
  31. package/dist/images/index.js +12 -12
  32. package/dist/images/index.mjs +1 -5
  33. package/dist/index.d.mts +2 -2
  34. package/dist/index.d.ts +2 -2
  35. package/dist/index.js +510 -106
  36. package/dist/index.js.map +1 -1
  37. package/dist/index.mjs +440 -13
  38. package/dist/index.mjs.map +1 -1
  39. package/dist/{routing-CIOFpFCB.d.mts → routing-BT0RrBLk.d.mts} +14 -1
  40. package/dist/{routing-CF91y6NO.d.ts → routing-wmNSxSvP.d.ts} +14 -1
  41. package/dist/seo/index.d.mts +37 -4
  42. package/dist/seo/index.d.ts +37 -4
  43. package/dist/seo/index.js +48 -18
  44. package/dist/seo/index.js.map +1 -1
  45. package/dist/seo/index.mjs +34 -5
  46. package/dist/seo/index.mjs.map +1 -1
  47. package/dist/seo/server.d.mts +15 -4
  48. package/dist/seo/server.d.ts +15 -4
  49. package/dist/seo/server.js +16 -16
  50. package/dist/seo/server.mjs +2 -2
  51. package/dist/{types-j8X4vUhB.d.mts → types-wf4dwNMO.d.mts} +5 -0
  52. package/dist/{types-j8X4vUhB.d.ts → types-wf4dwNMO.d.ts} +5 -0
  53. package/package.json +6 -1
  54. package/dist/api-QUIPJJCX.js.map +0 -1
  55. package/dist/api-V3BA5PMX.mjs.map +0 -1
  56. package/dist/chunk-FQVGK746.mjs.map +0 -1
  57. package/dist/chunk-JGQPAXTL.mjs.map +0 -1
  58. package/dist/chunk-MB3WR5KJ.mjs.map +0 -1
  59. package/dist/chunk-QQB4FO4Q.js.map +0 -1
  60. package/dist/chunk-TDK7DLCH.js.map +0 -1
  61. package/dist/chunk-VDI7KYME.js.map +0 -1
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var chunkVDI7KYME_js = require('./chunk-VDI7KYME.js');
3
+ var chunkIYVJGUYX_js = require('./chunk-IYVJGUYX.js');
4
4
 
5
5
  // src/seo/routing.ts
6
6
  async function getRedirect(options) {
7
7
  const { projectId, path } = options;
8
- const redirect = await chunkVDI7KYME_js.getRedirectData(projectId, path);
8
+ const redirect = await chunkIYVJGUYX_js.getRedirectData(projectId, path);
9
9
  if (!redirect) {
10
10
  return null;
11
11
  }
@@ -47,7 +47,7 @@ function parseRobotsString(robots) {
47
47
  }
48
48
  async function getRobotsDirective(options) {
49
49
  const { projectId, path } = options;
50
- const robotsString = await chunkVDI7KYME_js.getRobotsData(projectId, path);
50
+ const robotsString = await chunkIYVJGUYX_js.getRobotsData(projectId, path);
51
51
  if (!robotsString) {
52
52
  return { index: true, follow: true };
53
53
  }
@@ -55,7 +55,7 @@ async function getRobotsDirective(options) {
55
55
  }
56
56
  async function generateSitemap(options) {
57
57
  const { projectId, baseUrl, publishedOnly = true } = options;
58
- const pages = await chunkVDI7KYME_js.getSitemapEntries(projectId, { publishedOnly });
58
+ const pages = await chunkIYVJGUYX_js.getSitemapEntries(projectId, { publishedOnly });
59
59
  const normalizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
60
60
  return pages.map((page) => ({
61
61
  path: page.path,
@@ -66,7 +66,7 @@ async function generateSitemap(options) {
66
66
  }));
67
67
  }
68
68
  async function registerLocalSitemap(options) {
69
- const { registerSitemap } = await import('./api-QUIPJJCX.js');
69
+ const { registerSitemap } = await import('./api-UBHLAPUG.js');
70
70
  let entries = options.entries || [];
71
71
  if (options.autoDiscover && entries.length === 0) {
72
72
  try {
@@ -86,7 +86,10 @@ async function registerLocalSitemap(options) {
86
86
  return { success: true, created: 0, updated: 0 };
87
87
  }
88
88
  console.log(`[Uptrade] Registering ${entries.length} sitemap entries...`);
89
- const result = await registerSitemap(entries);
89
+ const result = await registerSitemap(entries, {
90
+ optimize_meta: options.optimize_meta !== false
91
+ // Default to true
92
+ });
90
93
  if (result.success) {
91
94
  console.log(`[Uptrade] Sitemap registered: ${result.created} new, ${result.updated} updated`);
92
95
  }
@@ -136,5 +139,5 @@ exports.getRedirect = getRedirect;
136
139
  exports.getRobotsDirective = getRobotsDirective;
137
140
  exports.isIndexable = isIndexable;
138
141
  exports.registerLocalSitemap = registerLocalSitemap;
139
- //# sourceMappingURL=chunk-QQB4FO4Q.js.map
140
- //# sourceMappingURL=chunk-QQB4FO4Q.js.map
142
+ //# sourceMappingURL=chunk-AWAJEIZS.js.map
143
+ //# sourceMappingURL=chunk-AWAJEIZS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/seo/routing.ts"],"names":["getRedirectData","getRobotsData","getSitemapEntries"],"mappings":"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","file":"chunk-AWAJEIZS.js","sourcesContent":["import { getRedirectData, getRobotsData, getSitemapEntries } from './api'\nimport type { \n GetRedirectOptions, \n RedirectResult, \n GetRobotsOptions, \n RobotsDirective,\n GetSitemapEntriesOptions,\n SitemapEntry \n} from './types'\n\n/**\n * Get redirect for a path if one exists\n * \n * Use in Next.js middleware to handle managed redirects\n * \n * @example\n * ```tsx\n * // middleware.ts\n * import { getRedirect } from '@uptrade/seo'\n * \n * export async function middleware(request) {\n * const redirect = await getRedirect({\n * projectId: process.env.UPTRADE_PROJECT_ID!,\n * path: request.nextUrl.pathname\n * })\n * \n * if (redirect) {\n * return NextResponse.redirect(redirect.destination, redirect.statusCode)\n * }\n * }\n * ```\n */\nexport async function getRedirect(\n options: GetRedirectOptions\n): Promise<RedirectResult | null> {\n const { projectId, path } = options\n\n const redirect = await getRedirectData(projectId, path)\n\n if (!redirect) {\n return null\n }\n\n // Check if expired\n if (redirect.expires_at && new Date(redirect.expires_at) < new Date()) {\n return null\n }\n\n // Determine destination\n const destination = redirect.destination_url || redirect.destination_path\n const isExternal = destination.startsWith('http://') || destination.startsWith('https://')\n\n return {\n destination,\n statusCode: redirect.status_code,\n isExternal,\n }\n}\n\n/**\n * Parse robots directive string into structured object\n */\nfunction parseRobotsString(robots: string): RobotsDirective {\n const directive: RobotsDirective = {\n index: true,\n follow: true,\n }\n\n const parts = robots.toLowerCase().split(',').map(p => p.trim())\n\n for (const part of parts) {\n if (part === 'noindex') directive.index = false\n if (part === 'nofollow') directive.follow = false\n if (part === 'noarchive') directive.noarchive = true\n if (part === 'nosnippet') directive.nosnippet = true\n if (part === 'noimageindex') directive.noimageindex = true\n if (part.startsWith('max-snippet:')) {\n directive.max_snippet = parseInt(part.split(':')[1], 10)\n }\n if (part.startsWith('max-image-preview:')) {\n const value = part.split(':')[1] as 'none' | 'standard' | 'large'\n directive.max_image_preview = value\n }\n if (part.startsWith('max-video-preview:')) {\n directive.max_video_preview = parseInt(part.split(':')[1], 10)\n }\n }\n\n return directive\n}\n\n/**\n * Get robots directive for a page\n * \n * @example\n * ```tsx\n * const robots = await getRobotsDirective({\n * projectId: process.env.UPTRADE_PROJECT_ID!,\n * path: '/private-page'\n * })\n * \n * if (!robots.index) {\n * // Page should not be indexed\n * }\n * ```\n */\nexport async function getRobotsDirective(\n options: GetRobotsOptions\n): Promise<RobotsDirective> {\n const { projectId, path } = options\n\n const robotsString = await getRobotsData(projectId, path)\n\n if (!robotsString) {\n // Default: index and follow\n return { index: true, follow: true }\n }\n\n return parseRobotsString(robotsString)\n}\n\n/**\n * Get sitemap entries for a project\n * \n * Use in sitemap.ts to generate dynamic sitemap\n * \n * @example\n * ```tsx\n * // app/sitemap.ts\n * import { generateSitemap } from '@uptrade/seo'\n * \n * export default async function sitemap() {\n * return generateSitemap({\n * projectId: process.env.UPTRADE_PROJECT_ID!,\n * baseUrl: 'https://example.com',\n * publishedOnly: true\n * })\n * }\n * ```\n */\nexport async function generateSitemap(\n options: GetSitemapEntriesOptions\n): Promise<SitemapEntry[]> {\n const { projectId, baseUrl, publishedOnly = true } = options\n\n const pages = await getSitemapEntries(projectId, { publishedOnly })\n\n const normalizedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl\n\n return pages.map(page => ({\n path: page.path,\n url: `${normalizedBase}${page.path}`,\n lastmod: page.updated_at,\n changefreq: page.sitemap_changefreq || 'weekly',\n priority: page.sitemap_priority || 0.5,\n }))\n}\n\n/**\n * Register local sitemap entries with Uptrade Portal\n * \n * Call this at build time to sync your local routes to seo_pages.\n * This ensures analytics only tracks real pages.\n * \n * After registration, Signal AI will generate optimized meta titles\n * and descriptions for pages that don't have managed meta yet.\n * \n * @example\n * ```ts\n * // scripts/register-sitemap.ts\n * import { registerLocalSitemap } from '@uptrade/seo'\n * \n * // Option 1: Provide entries directly\n * await registerLocalSitemap({\n * entries: [\n * { path: '/', title: 'Home', priority: 1.0 },\n * { path: '/about', title: 'About Us', priority: 0.8 },\n * ]\n * })\n * \n * // Option 2: Auto-discover from Next.js app directory\n * await registerLocalSitemap({ autoDiscover: true })\n * \n * // Option 3: Skip Signal AI meta optimization\n * await registerLocalSitemap({ autoDiscover: true, optimize_meta: false })\n * ```\n */\nexport async function registerLocalSitemap(options: {\n entries?: Array<{\n path: string\n title?: string\n priority?: number\n changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'\n }>\n autoDiscover?: boolean\n /** Trigger Signal AI to generate optimized meta titles/descriptions (default: true) */\n optimize_meta?: boolean\n}): Promise<{ \n success: boolean\n created: number\n updated: number\n removed?: number\n meta_optimization?: {\n triggered: boolean\n pages_queued: number\n } | null\n}> {\n const { registerSitemap } = await import('./api')\n \n let entries = options.entries || []\n \n // Auto-discover from Next.js app directory if requested\n if (options.autoDiscover && entries.length === 0) {\n try {\n const fs = await import('fs')\n const path = await import('path')\n \n const appDir = path.join(process.cwd(), 'app')\n if (fs.existsSync(appDir)) {\n entries = discoverNextJsRoutes(appDir, fs, path)\n console.log(`[Uptrade] Auto-discovered ${entries.length} routes from app directory`)\n }\n } catch (error) {\n console.error('[Uptrade] Auto-discovery failed:', error)\n }\n }\n \n if (entries.length === 0) {\n console.warn('[Uptrade] No sitemap entries to register')\n return { success: true, created: 0, updated: 0 }\n }\n \n console.log(`[Uptrade] Registering ${entries.length} sitemap entries...`)\n const result = await registerSitemap(entries, { \n optimize_meta: options.optimize_meta !== false, // Default to true\n })\n \n if (result.success) {\n console.log(`[Uptrade] Sitemap registered: ${result.created} new, ${result.updated} updated`)\n }\n \n return result\n}\n\n/**\n * Discover routes from Next.js app directory\n */\nfunction discoverNextJsRoutes(\n appDir: string,\n fs: typeof import('fs'),\n path: typeof import('path'),\n basePath: string = ''\n): Array<{ path: string; priority: number }> {\n const entries: Array<{ path: string; priority: number }> = []\n \n const items = fs.readdirSync(appDir, { withFileTypes: true })\n \n for (const item of items) {\n // Skip private folders, api routes, and special files\n if (item.name.startsWith('_') || item.name.startsWith('.')) continue\n if (item.name === 'api') continue\n if (item.name === 'node_modules') continue\n \n const itemPath = path.join(appDir, item.name)\n \n if (item.isDirectory()) {\n // Check for page.tsx/page.js in this directory\n const hasPage = fs.existsSync(path.join(itemPath, 'page.tsx')) ||\n fs.existsSync(path.join(itemPath, 'page.js')) ||\n fs.existsSync(path.join(itemPath, 'page.jsx'))\n \n // Handle route groups (parentheses)\n const isRouteGroup = item.name.startsWith('(') && item.name.endsWith(')')\n \n // Handle dynamic segments [slug]\n const isDynamic = item.name.startsWith('[') && item.name.endsWith(']')\n \n let routePath = basePath\n if (!isRouteGroup && !isDynamic) {\n routePath = `${basePath}/${item.name}`\n }\n \n if (hasPage && !isDynamic) {\n const priority = routePath === '' ? 1.0 : 0.8\n entries.push({ path: routePath || '/', priority })\n }\n \n // Recurse into subdirectories (but not dynamic ones)\n if (!isDynamic) {\n const subEntries = discoverNextJsRoutes(itemPath, fs, path, isRouteGroup ? basePath : routePath)\n entries.push(...subEntries)\n }\n }\n }\n \n // Add root if app/page.tsx exists and we're at root\n if (basePath === '') {\n const hasRootPage = fs.existsSync(path.join(appDir, 'page.tsx')) ||\n fs.existsSync(path.join(appDir, 'page.js')) ||\n fs.existsSync(path.join(appDir, 'page.jsx'))\n if (hasRootPage) {\n entries.unshift({ path: '/', priority: 1.0 })\n }\n }\n \n return entries\n}\n\n/**\n * Check if a path should be indexed\n * \n * Quick helper to check indexability without full directive parsing\n */\nexport async function isIndexable(\n projectId: string,\n path: string\n): Promise<boolean> {\n const directive = await getRobotsDirective({ projectId, path })\n return directive.index\n}\n"]}
@@ -1,442 +1,14 @@
1
- import { SitemapSync } from './chunk-WPSRS352.mjs';
2
- import { AnalyticsProvider } from './chunk-42EXHJTC.mjs';
3
- import { EngageWidget } from './chunk-DYM5ML2V.mjs';
4
- import { configureFormsApi } from './chunk-SMUFNQLM.mjs';
5
- import { createContext, useContext, useState, useRef, useCallback, useEffect, useMemo, Suspense } from 'react';
6
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
1
+ import { useState, useCallback, useEffect } from 'react';
2
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
7
3
 
8
- var SignalContext = createContext(null);
9
- function useSignal() {
10
- const context = useContext(SignalContext);
11
- if (!context) {
12
- throw new Error("useSignal must be used within a SignalBridge");
13
- }
14
- return context;
15
- }
16
- function getApiConfig() {
17
- const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || "https://api.uptrademedia.com" : "https://api.uptrademedia.com";
18
- const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
19
- return { apiUrl, apiKey };
20
- }
21
- function getVisitorId() {
22
- if (typeof window === "undefined") return "";
23
- const key = "_uptrade_vid";
24
- let visitorId = localStorage.getItem(key);
25
- if (!visitorId) {
26
- visitorId = crypto.randomUUID();
27
- localStorage.setItem(key, visitorId);
28
- }
29
- return visitorId;
30
- }
31
- function getSessionId() {
32
- if (typeof window === "undefined") return "";
33
- const key = "_uptrade_sid";
34
- let sessionId = sessionStorage.getItem(key);
35
- if (!sessionId) {
36
- sessionId = crypto.randomUUID();
37
- sessionStorage.setItem(key, sessionId);
38
- }
39
- return sessionId;
40
- }
41
- function getDeviceType() {
42
- if (typeof window === "undefined") return "desktop";
43
- const ua = navigator.userAgent;
44
- if (/tablet|ipad|playbook|silk/i.test(ua)) return "tablet";
45
- if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return "mobile";
46
- return "desktop";
47
- }
48
- function getBrowser() {
49
- if (typeof window === "undefined") return "unknown";
50
- const ua = navigator.userAgent;
51
- if (ua.includes("Firefox")) return "Firefox";
52
- if (ua.includes("Edg")) return "Edge";
53
- if (ua.includes("Chrome")) return "Chrome";
54
- if (ua.includes("Safari")) return "Safari";
55
- return "Other";
56
- }
57
- function getOS() {
58
- if (typeof window === "undefined") return "unknown";
59
- const ua = navigator.userAgent;
60
- if (ua.includes("Windows")) return "Windows";
61
- if (ua.includes("Mac")) return "macOS";
62
- if (ua.includes("iPhone") || ua.includes("iPad")) return "iOS";
63
- if (ua.includes("Android")) return "Android";
64
- if (ua.includes("Linux")) return "Linux";
65
- return "Other";
66
- }
67
- function SignalBridge({
68
- enabled = true,
69
- realtime = true,
70
- experiments = true,
71
- behaviorTracking = true,
72
- children
73
- }) {
74
- const [config, setConfig] = useState(null);
75
- const [loading, setLoading] = useState(true);
76
- const [error, setError] = useState(null);
77
- const eventSourceRef = useRef(null);
78
- const eventQueueRef = useRef([]);
79
- const flushTimeoutRef = useRef(null);
80
- const assignmentsRef = useRef(/* @__PURE__ */ new Map());
81
- const pageLoadTimeRef = useRef(Date.now());
82
- const scrollDepthRef = useRef(0);
83
- const clickCountRef = useRef(0);
84
- const { apiUrl, apiKey } = getApiConfig();
85
- const fetchConfig = useCallback(async () => {
86
- if (!apiKey || !enabled) {
87
- setLoading(false);
88
- return;
89
- }
90
- try {
91
- const response = await fetch(`${apiUrl}/api/public/signal/config`, {
92
- headers: {
93
- "x-api-key": apiKey,
94
- "x-visitor-id": getVisitorId()
95
- }
96
- });
97
- if (!response.ok) {
98
- throw new Error(`Failed to fetch Signal config: ${response.statusText}`);
99
- }
100
- const data = await response.json();
101
- setConfig(data.config);
102
- setError(null);
103
- if (experiments && data.config?.experiments) {
104
- for (const exp of data.config.experiments) {
105
- if (exp.status === "running") {
106
- await loadExperimentAssignment(exp.id);
107
- }
108
- }
109
- }
110
- } catch (err) {
111
- console.error("[Signal] Config fetch error:", err);
112
- setError(err);
113
- } finally {
114
- setLoading(false);
115
- }
116
- }, [apiUrl, apiKey, enabled, experiments]);
117
- const connectSSE = useCallback(() => {
118
- if (!apiKey || !enabled || !realtime) return;
119
- if (eventSourceRef.current) {
120
- eventSourceRef.current.close();
121
- }
122
- const url = `${apiUrl}/api/public/signal/stream?key=${apiKey}`;
123
- const eventSource = new EventSource(url);
124
- eventSource.addEventListener("config_update", (e) => {
125
- try {
126
- const { config: newConfig, version } = JSON.parse(e.data);
127
- setConfig((prev) => {
128
- if (prev?.version !== version) {
129
- console.log("[Signal] Config updated to version:", version);
130
- return newConfig;
131
- }
132
- return prev;
133
- });
134
- } catch (err) {
135
- console.error("[Signal] SSE parse error:", err);
136
- }
137
- });
138
- eventSource.addEventListener("experiment_update", (e) => {
139
- try {
140
- const { experiment_id, action } = JSON.parse(e.data);
141
- if (action === "started" || action === "updated") {
142
- loadExperimentAssignment(experiment_id);
143
- } else if (action === "stopped") {
144
- assignmentsRef.current.delete(experiment_id);
145
- }
146
- } catch (err) {
147
- console.error("[Signal] Experiment update error:", err);
148
- }
149
- });
150
- eventSource.onerror = () => {
151
- console.warn("[Signal] SSE connection error, reconnecting...");
152
- eventSource.close();
153
- setTimeout(connectSSE, 5e3);
154
- };
155
- eventSourceRef.current = eventSource;
156
- }, [apiUrl, apiKey, enabled, realtime]);
157
- const loadExperimentAssignment = useCallback(async (experimentId) => {
158
- const storageKey = `_signal_exp_${experimentId}`;
159
- const stored = localStorage.getItem(storageKey);
160
- if (stored) {
161
- try {
162
- const assignment = JSON.parse(stored);
163
- if (assignment.expires > Date.now()) {
164
- assignmentsRef.current.set(experimentId, assignment);
165
- return assignment;
166
- }
167
- } catch {
168
- }
169
- }
170
- try {
171
- const response = await fetch(`${apiUrl}/api/public/signal/experiment/${experimentId}`, {
172
- headers: {
173
- "x-api-key": apiKey,
174
- "x-visitor-id": getVisitorId()
175
- }
176
- });
177
- if (!response.ok) return null;
178
- const assignment = await response.json();
179
- localStorage.setItem(storageKey, JSON.stringify(assignment));
180
- assignmentsRef.current.set(experimentId, assignment);
181
- return assignment;
182
- } catch (err) {
183
- console.error("[Signal] Experiment assignment error:", err);
184
- return null;
185
- }
186
- }, [apiUrl, apiKey]);
187
- const getExperiment = useCallback((experimentId) => {
188
- return assignmentsRef.current.get(experimentId) || null;
189
- }, []);
190
- const flushEvents = useCallback(async () => {
191
- if (eventQueueRef.current.length === 0) return;
192
- const events = [...eventQueueRef.current];
193
- eventQueueRef.current = [];
194
- try {
195
- await fetch(`${apiUrl}/api/public/signal/events`, {
196
- method: "POST",
197
- headers: {
198
- "Content-Type": "application/json",
199
- "x-api-key": apiKey
200
- },
201
- body: JSON.stringify({
202
- visitor_id: getVisitorId(),
203
- session_id: getSessionId(),
204
- events
205
- })
206
- });
207
- } catch (err) {
208
- eventQueueRef.current = [...events, ...eventQueueRef.current];
209
- console.error("[Signal] Event flush error:", err);
210
- }
211
- }, [apiUrl, apiKey]);
212
- const trackEvent = useCallback((event) => {
213
- if (!enabled || !apiKey) return;
214
- const enrichedEvent = {
215
- ...event,
216
- page_url: window.location.href,
217
- page_path: window.location.pathname,
218
- referrer: document.referrer,
219
- device_type: getDeviceType(),
220
- browser: getBrowser(),
221
- os: getOS(),
222
- viewport_width: window.innerWidth,
223
- viewport_height: window.innerHeight,
224
- time_on_page: Date.now() - pageLoadTimeRef.current,
225
- scroll_depth: scrollDepthRef.current,
226
- click_count: clickCountRef.current,
227
- experiments: Array.from(assignmentsRef.current.values()).map((a) => ({
228
- experiment_id: a.experiment_id,
229
- variant_key: a.variant_key
230
- })),
231
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
232
- };
233
- eventQueueRef.current.push(enrichedEvent);
234
- if (flushTimeoutRef.current) {
235
- clearTimeout(flushTimeoutRef.current);
236
- }
237
- flushTimeoutRef.current = setTimeout(flushEvents, 1e3);
238
- }, [enabled, apiKey, flushEvents]);
239
- const trackOutcome = useCallback(async (outcome) => {
240
- if (!enabled || !apiKey) return;
241
- try {
242
- await fetch(`${apiUrl}/api/public/signal/outcome`, {
243
- method: "POST",
244
- headers: {
245
- "Content-Type": "application/json",
246
- "x-api-key": apiKey
247
- },
248
- body: JSON.stringify({
249
- ...outcome,
250
- visitor_id: getVisitorId(),
251
- session_id: getSessionId(),
252
- experiments: Array.from(assignmentsRef.current.keys()),
253
- page_url: window.location.href,
254
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
255
- })
256
- });
257
- } catch (err) {
258
- console.error("[Signal] Outcome tracking error:", err);
259
- }
260
- }, [apiUrl, apiKey, enabled]);
261
- useEffect(() => {
262
- if (!behaviorTracking || typeof window === "undefined") return;
263
- const handleScroll = () => {
264
- const scrollTop = window.scrollY;
265
- const docHeight = document.documentElement.scrollHeight - window.innerHeight;
266
- const depth = docHeight > 0 ? Math.round(scrollTop / docHeight * 100) : 0;
267
- scrollDepthRef.current = Math.max(scrollDepthRef.current, depth);
268
- };
269
- const handleClick = () => {
270
- clickCountRef.current++;
271
- };
272
- window.addEventListener("scroll", handleScroll, { passive: true });
273
- window.addEventListener("click", handleClick);
274
- const handleVisibilityChange = () => {
275
- if (document.visibilityState === "hidden") {
276
- flushEvents();
277
- }
278
- };
279
- document.addEventListener("visibilitychange", handleVisibilityChange);
280
- window.addEventListener("beforeunload", flushEvents);
281
- return () => {
282
- window.removeEventListener("scroll", handleScroll);
283
- window.removeEventListener("click", handleClick);
284
- document.removeEventListener("visibilitychange", handleVisibilityChange);
285
- window.removeEventListener("beforeunload", flushEvents);
286
- };
287
- }, [behaviorTracking, flushEvents]);
288
- useEffect(() => {
289
- fetchConfig();
290
- }, [fetchConfig]);
291
- useEffect(() => {
292
- if (config && realtime) {
293
- connectSSE();
294
- }
295
- return () => {
296
- if (eventSourceRef.current) {
297
- eventSourceRef.current.close();
298
- }
299
- };
300
- }, [config, realtime, connectSSE]);
301
- const contextValue = useMemo(() => ({
302
- config,
303
- loading,
304
- error,
305
- trackEvent,
306
- trackOutcome,
307
- getExperiment,
308
- refreshConfig: fetchConfig
309
- }), [config, loading, error, trackEvent, trackOutcome, getExperiment, fetchConfig]);
310
- return /* @__PURE__ */ jsx(SignalContext.Provider, { value: contextValue, children });
311
- }
312
- function useSignalConfig() {
313
- const { config } = useSignal();
314
- return config;
315
- }
316
- function useSignalEvent() {
317
- const { trackEvent } = useSignal();
318
- return trackEvent;
319
- }
320
- function useSignalOutcome() {
321
- const { trackOutcome } = useSignal();
322
- return { trackOutcome };
323
- }
324
- function useSignalExperiment(experimentId) {
325
- const { getExperiment, config } = useSignal();
326
- const assignment = getExperiment(experimentId);
327
- const experiment = config?.experiments?.find((e) => e.id === experimentId);
328
- const isRunning = experiment?.status === "running";
329
- return {
330
- assignment: isRunning ? assignment : null,
331
- variant: isRunning && assignment ? assignment.variant_key : null,
332
- isControl: !assignment || assignment.variant_key === "control"
333
- };
334
- }
335
- var SiteKitContext = createContext(null);
336
- function useSiteKit() {
337
- const context = useContext(SiteKitContext);
338
- if (!context) {
339
- throw new Error("useSiteKit must be used within a SiteKitProvider");
340
- }
341
- return context;
342
- }
343
- function SiteKitProvider({
344
- children,
345
- apiKey,
346
- apiUrl,
347
- signalUrl,
348
- analytics,
349
- engage,
350
- forms,
351
- signal,
352
- debug = false
353
- }) {
354
- const finalApiUrl = apiUrl || "https://api.uptrademedia.com";
355
- const finalSignalUrl = signalUrl || "https://signal.uptrademedia.com";
356
- if (!apiKey) {
357
- console.error("@uptrade/site-kit: No API key provided. Set NEXT_PUBLIC_UPTRADE_API_KEY environment variable.");
358
- }
359
- useEffect(() => {
360
- if (typeof window !== "undefined") {
361
- window.__SITE_KIT_API_URL__ = finalApiUrl;
362
- window.__SITE_KIT_SIGNAL_URL__ = finalSignalUrl;
363
- window.__SITE_KIT_API_KEY__ = apiKey;
364
- window.__SITE_KIT_DEBUG__ = debug;
365
- }
366
- if (apiKey) {
367
- configureFormsApi({
368
- baseUrl: finalApiUrl,
369
- apiKey
370
- });
371
- }
372
- }, [finalApiUrl, finalSignalUrl, apiKey, debug]);
373
- const contextValue = useMemo(
374
- () => ({
375
- apiUrl: finalApiUrl,
376
- signalUrl: finalSignalUrl,
377
- apiKey,
378
- analytics,
379
- engage,
380
- forms,
381
- signal,
382
- debug,
383
- isReady: true
384
- }),
385
- [finalApiUrl, finalSignalUrl, apiKey, analytics, engage, forms, signal, debug]
386
- );
387
- let content = /* @__PURE__ */ jsx(Fragment, { children });
388
- if (signal?.enabled) {
389
- content = /* @__PURE__ */ jsx(
390
- SignalBridge,
391
- {
392
- enabled: signal.enabled,
393
- realtime: signal.realtime !== false,
394
- experiments: signal.experiments !== false,
395
- behaviorTracking: signal.behaviorTracking !== false,
396
- children: content
397
- }
398
- );
399
- }
400
- if (analytics?.enabled) {
401
- content = /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
402
- AnalyticsProvider,
403
- {
404
- apiUrl: finalApiUrl,
405
- apiKey,
406
- trackPageViews: analytics.trackPageViews !== false,
407
- trackWebVitals: analytics.trackWebVitals !== false,
408
- trackScrollDepth: analytics.trackScrollDepth !== false,
409
- trackClicks: analytics.trackClicks !== false,
410
- debug,
411
- children: content
412
- }
413
- ) });
414
- }
415
- if (engage?.enabled) {
416
- content = /* @__PURE__ */ jsxs(Fragment, { children: [
417
- content,
418
- /* @__PURE__ */ jsx(
419
- EngageWidget,
420
- {
421
- apiUrl: finalApiUrl,
422
- apiKey,
423
- position: engage.position || "bottom-right",
424
- chatEnabled: engage.chatEnabled !== false
425
- }
426
- )
427
- ] });
428
- }
429
- content = /* @__PURE__ */ jsxs(Fragment, { children: [
430
- content,
431
- /* @__PURE__ */ jsx(SitemapSync, { debug })
432
- ] });
433
- return /* @__PURE__ */ jsx(SiteKitContext.Provider, { value: contextValue, children: content });
434
- }
4
+ // src/images/ManagedImage.tsx
435
5
  var isDevMode = () => {
436
6
  if (typeof window === "undefined") return false;
437
7
  return process.env.NODE_ENV === "development" || window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.search.includes("uptrade_dev=true");
438
8
  };
439
9
  function ManagedImage({
10
+ apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY,
11
+ apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || "https://api.uptrademedia.com",
440
12
  slotId,
441
13
  pagePath,
442
14
  alt,
@@ -452,7 +24,6 @@ function ManagedImage({
452
24
  style,
453
25
  forceDevMode
454
26
  }) {
455
- const context = useSiteKit();
456
27
  const [imageData, setImageData] = useState(null);
457
28
  const [loading, setLoading] = useState(true);
458
29
  const [error, setError] = useState(null);
@@ -460,18 +31,18 @@ function ManagedImage({
460
31
  const [devMode] = useState(() => forceDevMode || isDevMode());
461
32
  const currentPath = pagePath ?? (typeof window !== "undefined" ? window.location.pathname : "/");
462
33
  const fetchImage = useCallback(async () => {
463
- if (!context?.apiKey || !context?.apiUrl) {
34
+ if (!apiKey || !apiUrl) {
464
35
  setLoading(false);
465
36
  return;
466
37
  }
467
38
  try {
468
39
  const params = new URLSearchParams({ page_path: currentPath });
469
40
  const res = await fetch(
470
- `${context.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,
41
+ `${apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,
471
42
  {
472
43
  headers: {
473
44
  "Content-Type": "application/json",
474
- "x-api-key": context.apiKey
45
+ "x-api-key": apiKey
475
46
  }
476
47
  }
477
48
  );
@@ -488,7 +59,7 @@ function ManagedImage({
488
59
  } finally {
489
60
  setLoading(false);
490
61
  }
491
- }, [context?.apiKey, context?.apiUrl, slotId, currentPath, onError]);
62
+ }, [apiKey, apiUrl, slotId, currentPath, onError]);
492
63
  useEffect(() => {
493
64
  fetchImage();
494
65
  }, [fetchImage]);
@@ -562,7 +133,7 @@ function ManagedImage({
562
133
  {
563
134
  slotId,
564
135
  pagePath: currentPath,
565
- config: context,
136
+ config: { apiKey, apiUrl },
566
137
  onClose: () => setShowPicker(false),
567
138
  onSelect: () => {
568
139
  setShowPicker(false);
@@ -611,7 +182,7 @@ function ManagedImage({
611
182
  {
612
183
  slotId,
613
184
  pagePath: currentPath,
614
- config: context,
185
+ config: { apiKey, apiUrl },
615
186
  currentImage: imageData,
616
187
  onClose: () => setShowPicker(false),
617
188
  onSelect: () => {
@@ -866,6 +437,53 @@ function ImagePickerModal({
866
437
  }
867
438
  );
868
439
  }
440
+ async function fetchFaviconData(apiUrl, apiKey) {
441
+ try {
442
+ const res = await fetch(`${apiUrl}/public/images/slot/favicon`, {
443
+ headers: {
444
+ "Content-Type": "application/json",
445
+ "x-api-key": apiKey
446
+ },
447
+ cache: "no-store"
448
+ // Always fetch fresh
449
+ });
450
+ if (!res.ok) {
451
+ console.warn("[ManagedFavicon] Failed to fetch:", res.status);
452
+ return null;
453
+ }
454
+ const data = await res.json();
455
+ if (data.image && !data.is_placeholder) {
456
+ return {
457
+ public_url: data.image.public_url || data.image.external_url,
458
+ mime_type: data.image.file?.mime_type,
459
+ is_placeholder: false
460
+ };
461
+ }
462
+ return null;
463
+ } catch (err) {
464
+ console.error("[ManagedFavicon] Error fetching favicon:", err);
465
+ return null;
466
+ }
467
+ }
468
+ async function ManagedFavicon({
469
+ apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY,
470
+ apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || "https://api.uptrademedia.com",
471
+ fallback = "/favicon.ico",
472
+ themeColor = "#4bbf39"
473
+ }) {
474
+ const faviconData = apiKey && apiUrl ? await fetchFaviconData(apiUrl, apiKey) : null;
475
+ const faviconUrl = faviconData?.public_url || fallback;
476
+ const mimeType = faviconData?.mime_type || "image/x-icon";
477
+ const isSvg = mimeType === "image/svg+xml" || faviconUrl.endsWith(".svg");
478
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
479
+ isSvg ? /* @__PURE__ */ jsx("link", { rel: "icon", type: "image/svg+xml", href: faviconUrl }) : /* @__PURE__ */ jsxs(Fragment, { children: [
480
+ /* @__PURE__ */ jsx("link", { rel: "icon", type: "image/png", sizes: "32x32", href: faviconUrl }),
481
+ /* @__PURE__ */ jsx("link", { rel: "icon", type: "image/png", sizes: "16x16", href: faviconUrl })
482
+ ] }),
483
+ /* @__PURE__ */ jsx("link", { rel: "apple-touch-icon", sizes: "180x180", href: faviconUrl }),
484
+ /* @__PURE__ */ jsx("meta", { name: "theme-color", content: themeColor })
485
+ ] });
486
+ }
869
487
 
870
488
  // src/images/api.ts
871
489
  async function fetchManagedImage(config, slotId, pagePath) {
@@ -986,6 +604,6 @@ async function clearImageSlot(config, slotId, pagePath) {
986
604
  return res.json();
987
605
  }
988
606
 
989
- export { ManagedImage, SignalBridge, SiteKitProvider, assignImageToSlot, clearImageSlot, fetchManagedImage, fetchManagedImages, listImageFiles, uploadImage, useSignal, useSignalConfig, useSignalEvent, useSignalExperiment, useSignalOutcome, useSiteKit };
990
- //# sourceMappingURL=chunk-MB3WR5KJ.mjs.map
991
- //# sourceMappingURL=chunk-MB3WR5KJ.mjs.map
607
+ export { ManagedFavicon, ManagedImage, assignImageToSlot, clearImageSlot, fetchManagedImage, fetchManagedImages, listImageFiles, uploadImage };
608
+ //# sourceMappingURL=chunk-CDJL2YGL.mjs.map
609
+ //# sourceMappingURL=chunk-CDJL2YGL.mjs.map