@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.
- package/dist/api-CWtoFJCO.d.mts +137 -0
- package/dist/api-CWtoFJCO.d.ts +137 -0
- package/dist/{api-QUIPJJCX.js → api-UBHLAPUG.js} +20 -20
- package/dist/api-UBHLAPUG.js.map +1 -0
- package/dist/{api-V3BA5PMX.mjs → api-XNF6Q5HO.mjs} +3 -3
- package/dist/api-XNF6Q5HO.mjs.map +1 -0
- package/dist/blog/index.d.mts +131 -5
- package/dist/blog/index.d.ts +131 -5
- package/dist/blog/index.js +532 -302
- package/dist/blog/index.js.map +1 -1
- package/dist/blog/index.mjs +532 -302
- package/dist/blog/index.mjs.map +1 -1
- package/dist/{chunk-QQB4FO4Q.js → chunk-AWAJEIZS.js} +11 -8
- package/dist/chunk-AWAJEIZS.js.map +1 -0
- package/dist/{chunk-MB3WR5KJ.mjs → chunk-CDJL2YGL.mjs} +61 -443
- package/dist/chunk-CDJL2YGL.mjs.map +1 -0
- package/dist/{chunk-TDK7DLCH.js → chunk-FLAA4ZJO.js} +59 -448
- package/dist/chunk-FLAA4ZJO.js.map +1 -0
- package/dist/{chunk-JGQPAXTL.mjs → chunk-H5AGHERY.mjs} +8 -5
- package/dist/chunk-H5AGHERY.mjs.map +1 -0
- package/dist/{chunk-VDI7KYME.js → chunk-IYVJGUYX.js} +8 -4
- package/dist/chunk-IYVJGUYX.js.map +1 -0
- package/dist/{chunk-FQVGK746.mjs → chunk-SKHOW2CI.mjs} +8 -4
- package/dist/chunk-SKHOW2CI.mjs.map +1 -0
- package/dist/cli/index.js +32 -25
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +32 -25
- package/dist/cli/index.mjs.map +1 -1
- package/dist/images/index.d.mts +49 -126
- package/dist/images/index.d.ts +49 -126
- package/dist/images/index.js +12 -12
- package/dist/images/index.mjs +1 -5
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +510 -106
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +440 -13
- package/dist/index.mjs.map +1 -1
- package/dist/{routing-CIOFpFCB.d.mts → routing-BT0RrBLk.d.mts} +14 -1
- package/dist/{routing-CF91y6NO.d.ts → routing-wmNSxSvP.d.ts} +14 -1
- package/dist/seo/index.d.mts +37 -4
- package/dist/seo/index.d.ts +37 -4
- package/dist/seo/index.js +48 -18
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/index.mjs +34 -5
- package/dist/seo/index.mjs.map +1 -1
- package/dist/seo/server.d.mts +15 -4
- package/dist/seo/server.d.ts +15 -4
- package/dist/seo/server.js +16 -16
- package/dist/seo/server.mjs +2 -2
- package/dist/{types-j8X4vUhB.d.mts → types-wf4dwNMO.d.mts} +5 -0
- package/dist/{types-j8X4vUhB.d.ts → types-wf4dwNMO.d.ts} +5 -0
- package/package.json +6 -1
- package/dist/api-QUIPJJCX.js.map +0 -1
- package/dist/api-V3BA5PMX.mjs.map +0 -1
- package/dist/chunk-FQVGK746.mjs.map +0 -1
- package/dist/chunk-JGQPAXTL.mjs.map +0 -1
- package/dist/chunk-MB3WR5KJ.mjs.map +0 -1
- package/dist/chunk-QQB4FO4Q.js.map +0 -1
- package/dist/chunk-TDK7DLCH.js.map +0 -1
- package/dist/chunk-VDI7KYME.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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-
|
|
140
|
-
//# sourceMappingURL=chunk-
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
`${
|
|
41
|
+
`${apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,
|
|
471
42
|
{
|
|
472
43
|
headers: {
|
|
473
44
|
"Content-Type": "application/json",
|
|
474
|
-
"x-api-key":
|
|
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
|
-
}, [
|
|
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:
|
|
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:
|
|
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 {
|
|
990
|
-
//# sourceMappingURL=chunk-
|
|
991
|
-
//# sourceMappingURL=chunk-
|
|
607
|
+
export { ManagedFavicon, ManagedImage, assignImageToSlot, clearImageSlot, fetchManagedImage, fetchManagedImages, listImageFiles, uploadImage };
|
|
608
|
+
//# sourceMappingURL=chunk-CDJL2YGL.mjs.map
|
|
609
|
+
//# sourceMappingURL=chunk-CDJL2YGL.mjs.map
|