@sonordev/agency-site-kit 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{SiteArchitectureSection-EE6VQSXM.cjs → SiteArchitectureSection-J7BPBERH.cjs} +2 -2
- package/dist/SiteArchitectureSection-J7BPBERH.cjs.map +1 -0
- package/dist/{SiteArchitectureSection-PBBRTARV.js → SiteArchitectureSection-TE22OAWV.js} +2 -2
- package/dist/SiteArchitectureSection-TE22OAWV.js.map +1 -0
- package/dist/chunk-633UMQQH.cjs +31 -0
- package/dist/chunk-633UMQQH.cjs.map +1 -0
- package/dist/{chunk-GZXXGWHE.cjs → chunk-AVCH4LMG.cjs} +5 -4
- package/dist/chunk-AVCH4LMG.cjs.map +1 -0
- package/dist/{chunk-VVPJ4UFO.js → chunk-AYBUHRFS.js} +5 -4
- package/dist/chunk-AYBUHRFS.js.map +1 -0
- package/dist/chunk-EE75WRCM.cjs +56 -0
- package/dist/chunk-EE75WRCM.cjs.map +1 -0
- package/dist/{chunk-OWGVZNCO.js → chunk-RXBK2EOE.js} +5 -11
- package/dist/chunk-RXBK2EOE.js.map +1 -0
- package/dist/chunk-TVFXN6W6.js +52 -0
- package/dist/chunk-TVFXN6W6.js.map +1 -0
- package/dist/chunk-VVTK4OL6.js +28 -0
- package/dist/chunk-VVTK4OL6.js.map +1 -0
- package/dist/{chunk-EN6QQMKQ.cjs → chunk-WNR4KNG4.cjs} +6 -12
- package/dist/chunk-WNR4KNG4.cjs.map +1 -0
- package/dist/index.cjs +15 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -13
- package/dist/index.d.ts +28 -13
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/layout/index.cjs +3 -3
- package/dist/layout/index.js +2 -2
- package/dist/portfolio/index.cjs +4 -3
- package/dist/portfolio/index.js +2 -1
- package/dist/portfolio/server.cjs +8 -3
- package/dist/portfolio/server.cjs.map +1 -1
- package/dist/portfolio/server.js +7 -2
- package/dist/portfolio/server.js.map +1 -1
- package/package.json +3 -1
- package/dist/SiteArchitectureSection-EE6VQSXM.cjs.map +0 -1
- package/dist/SiteArchitectureSection-PBBRTARV.js.map +0 -1
- package/dist/chunk-EN6QQMKQ.cjs.map +0 -1
- package/dist/chunk-GZXXGWHE.cjs.map +0 -1
- package/dist/chunk-OWGVZNCO.js.map +0 -1
- package/dist/chunk-RVNASHPZ.cjs +0 -74
- package/dist/chunk-RVNASHPZ.cjs.map +0 -1
- package/dist/chunk-VVPJ4UFO.js.map +0 -1
- package/dist/chunk-ZEAOULCH.js +0 -70
- package/dist/chunk-ZEAOULCH.js.map +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var server = require('@sonordev/site-kit/server');
|
|
4
|
+
|
|
5
|
+
// src/shared/api.ts
|
|
6
|
+
var DEFAULT_REVALIDATE = 3600;
|
|
7
|
+
var LOG_PREFIX = "[@sonordev/agency-site-kit]";
|
|
8
|
+
function getApiConfig() {
|
|
9
|
+
const config = server.resolveServerApiConfig();
|
|
10
|
+
if (!config) {
|
|
11
|
+
console.warn(
|
|
12
|
+
`${LOG_PREFIX} Missing SONOR_API_KEY environment variable. Portfolio data will not be fetched.`
|
|
13
|
+
);
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return config;
|
|
17
|
+
}
|
|
18
|
+
async function apiFetch(path, options = {}) {
|
|
19
|
+
const {
|
|
20
|
+
revalidate = DEFAULT_REVALIDATE,
|
|
21
|
+
tags,
|
|
22
|
+
headers,
|
|
23
|
+
apiKey,
|
|
24
|
+
apiUrl,
|
|
25
|
+
method,
|
|
26
|
+
body,
|
|
27
|
+
...init
|
|
28
|
+
} = options;
|
|
29
|
+
return server.serverApiFetch(path, {
|
|
30
|
+
method,
|
|
31
|
+
body: body ?? void 0,
|
|
32
|
+
headers,
|
|
33
|
+
revalidate,
|
|
34
|
+
tags,
|
|
35
|
+
apiKey,
|
|
36
|
+
apiUrl,
|
|
37
|
+
retry: false,
|
|
38
|
+
requireKey: false,
|
|
39
|
+
logPrefix: LOG_PREFIX,
|
|
40
|
+
init
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async function apiGet(path, revalidate, apiKey, apiUrl) {
|
|
44
|
+
return apiFetch(path, {
|
|
45
|
+
method: "GET",
|
|
46
|
+
revalidate,
|
|
47
|
+
apiKey,
|
|
48
|
+
apiUrl
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
exports.apiFetch = apiFetch;
|
|
53
|
+
exports.apiGet = apiGet;
|
|
54
|
+
exports.getApiConfig = getApiConfig;
|
|
55
|
+
//# sourceMappingURL=chunk-EE75WRCM.cjs.map
|
|
56
|
+
//# sourceMappingURL=chunk-EE75WRCM.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/api.ts"],"names":["resolveServerApiConfig","serverApiFetch"],"mappings":";;;;;AAyBA,IAAM,kBAAA,GAAqB,IAAA;AAC3B,IAAM,UAAA,GAAa,6BAAA;AASZ,SAAS,YAAA,GAAiC;AAC/C,EAAA,MAAM,SAASA,6BAAA,EAAuB;AAEtC,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,GAAG,UAAU,CAAA,gFAAA;AAAA,KAEf;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO,MAAA;AACT;AAgCA,eAAsB,QAAA,CACpB,IAAA,EACA,OAAA,GAA2B,EAAC,EACT;AACnB,EAAA,MAAM;AAAA,IACJ,UAAA,GAAa,kBAAA;AAAA,IACb,IAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA;AAAA,IACA,GAAG;AAAA,GACL,GAAI,OAAA;AAEJ,EAAA,OAAOC,sBAAkB,IAAA,EAAM;AAAA,IAC7B,MAAA;AAAA,IACA,MAAM,IAAA,IAAQ,MAAA;AAAA,IACd,OAAA;AAAA,IACA,UAAA;AAAA,IACA,IAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,KAAA,EAAO,KAAA;AAAA,IACP,UAAA,EAAY,KAAA;AAAA,IACZ,SAAA,EAAW,UAAA;AAAA,IACX;AAAA,GACD,CAAA;AACH;AAUA,eAAsB,MAAA,CACpB,IAAA,EACA,UAAA,EACA,MAAA,EACA,MAAA,EACmB;AACnB,EAAA,OAAO,SAAY,IAAA,EAAM;AAAA,IACvB,MAAA,EAAQ,KAAA;AAAA,IACR,UAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH","file":"chunk-EE75WRCM.cjs","sourcesContent":["/**\n * @sonordev/agency-site-kit — API fetch utilities\n *\n * Server-side only. Thin adapters over core `@sonordev/site-kit`'s shared server\n * fetcher (`@sonordev/site-kit/server`) — the single source of truth for the\n * Sonor server data plane. This package no longer ships its own `apiFetch` copy;\n * it reuses core's so the two can never drift (monorepo CLAUDE.md,\n * \"Consolidate Duplicated Logic\").\n *\n * Config resolution (SONOR_API_KEY → `x-api-key`, SONOR_API_URL, the legacy\n * UPTRADE_API_KEY fallback) and the `https://api.sonor.io` base all live in core.\n *\n * NOTE: importing this module pulls core's `server-only` guard, so it (and any\n * barrel that re-exports it, including the package root and AgencySiteKitLayout)\n * is server-only. Client components must import from\n * `@sonordev/agency-site-kit/portfolio/client`.\n */\n\nimport {\n resolveServerApiConfig,\n serverApiFetch,\n type ServerApiConfig,\n} from '@sonordev/site-kit/server';\n\n/** Portfolio fetches default to a 1h ISR window (overridable per call). */\nconst DEFAULT_REVALIDATE = 3600;\nconst LOG_PREFIX = '[@sonordev/agency-site-kit]';\n\ntype ApiConfig = ServerApiConfig;\n\n/**\n * Reads API configuration from overrides → environment (delegates to core's\n * `resolveServerApiConfig`). Returns null and warns when no API key is found, so\n * portfolio data degrades gracefully instead of throwing.\n */\nexport function getApiConfig(): ApiConfig | null {\n const config = resolveServerApiConfig();\n\n if (!config) {\n console.warn(\n `${LOG_PREFIX} Missing SONOR_API_KEY environment variable. ` +\n 'Portfolio data will not be fetched.',\n );\n return null;\n }\n\n return config;\n}\n\ninterface ApiFetchOptions extends Omit<RequestInit, 'headers'> {\n /** ISR revalidation interval in seconds. Defaults to 3600. Set to 0 for no caching. */\n revalidate?: number;\n /**\n * Next.js cache tags for on-demand invalidation. Portfolio fetches use\n * PORTFOLIO_CACHE_TAG so the Portal can bust list + detail + sitemap data\n * with a single revalidateTag call (see ./revalidate).\n */\n tags?: string[];\n /** Additional headers to merge with defaults */\n headers?: Record<string, string>;\n /** Override API key (e.g. passed from layout props) */\n apiKey?: string;\n /** Override API URL */\n apiUrl?: string;\n}\n\n/**\n * Fetch wrapper for the public portfolio API — delegates to core's\n * `serverApiFetch`.\n *\n * - Injects `x-api-key` automatically\n * - Parses JSON responses\n * - Uses Next.js ISR caching via `next.revalidate` (+ optional cache `tags`)\n * - Returns null on failure (or a missing key) instead of throwing\n *\n * Single-attempt by design — portfolio pages degrade gracefully rather than\n * hard-failing. (Core's fetcher supports retry/backoff; this preserves\n * agency-site-kit's original behavior.)\n */\nexport async function apiFetch<T>(\n path: string,\n options: ApiFetchOptions = {},\n): Promise<T | null> {\n const {\n revalidate = DEFAULT_REVALIDATE,\n tags,\n headers,\n apiKey,\n apiUrl,\n method,\n body,\n ...init\n } = options;\n\n return serverApiFetch<T>(path, {\n method,\n body: body ?? undefined,\n headers,\n revalidate,\n tags,\n apiKey,\n apiUrl,\n retry: false,\n requireKey: false,\n logPrefix: LOG_PREFIX,\n init: init as RequestInit,\n });\n}\n\n/**\n * GET shorthand for apiFetch.\n *\n * @param path API path (e.g. \"/api/public/portfolio/items\")\n * @param revalidate ISR revalidation in seconds (default 3600)\n * @param apiKey Optional override API key\n * @param apiUrl Optional override API URL\n */\nexport async function apiGet<T>(\n path: string,\n revalidate?: number,\n apiKey?: string,\n apiUrl?: string,\n): Promise<T | null> {\n return apiFetch<T>(path, {\n method: 'GET',\n revalidate,\n apiKey,\n apiUrl,\n });\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { apiGet } from './chunk-
|
|
1
|
+
import { apiGet } from './chunk-TVFXN6W6.js';
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { resolveApiOrigin } from '@sonordev/site-kit/server';
|
|
3
4
|
|
|
4
5
|
// src/shared/brand.ts
|
|
5
6
|
function hexToRgb(hex) {
|
|
@@ -141,13 +142,6 @@ function mergeBrandConfig(partial) {
|
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
// src/layout/AgencySiteKitLayout.tsx
|
|
144
|
-
function portalApiOrigin(apiUrl) {
|
|
145
|
-
try {
|
|
146
|
-
return new URL(apiUrl.replace(/\/$/, "")).origin;
|
|
147
|
-
} catch {
|
|
148
|
-
return "https://api.sonor.io";
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
145
|
async function fetchBrandConfig(apiKey, apiUrl) {
|
|
152
146
|
const response = await apiGet(
|
|
153
147
|
"/api/public/portfolio/config",
|
|
@@ -172,7 +166,7 @@ async function AgencySiteKitLayout({
|
|
|
172
166
|
const brandConfig = brandConfigOverride ? mergeBrandConfig(brandConfigOverride) : await fetchBrandConfig(apiKey, apiUrl);
|
|
173
167
|
const cssString = generateBrandCSS(brandConfig);
|
|
174
168
|
const resolvedApiUrl = apiUrl ?? process.env.SONOR_API_URL ?? process.env.UPTRADE_API_URL ?? "https://api.sonor.io";
|
|
175
|
-
const preconnectHref =
|
|
169
|
+
const preconnectHref = resolveApiOrigin(resolvedApiUrl);
|
|
176
170
|
return React.createElement(
|
|
177
171
|
React.Fragment,
|
|
178
172
|
null,
|
|
@@ -212,5 +206,5 @@ async function AgencySiteKitLayout({
|
|
|
212
206
|
}
|
|
213
207
|
|
|
214
208
|
export { AgencySiteKitLayout, DEFAULT_BRAND_CONFIG, DEFAULT_DARK_MODE, generateBrandCSS, hexToRgb, mergeBrandConfig };
|
|
215
|
-
//# sourceMappingURL=chunk-
|
|
216
|
-
//# sourceMappingURL=chunk-
|
|
209
|
+
//# sourceMappingURL=chunk-RXBK2EOE.js.map
|
|
210
|
+
//# sourceMappingURL=chunk-RXBK2EOE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/brand.ts","../src/layout/AgencySiteKitLayout.tsx"],"names":[],"mappings":"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","file":"chunk-RXBK2EOE.js","sourcesContent":["/**\n * @sonordev/agency-site-kit — Brand customization system\n *\n * Generates CSS custom properties from a BrandConfig object.\n * Supports light/dark mode with automatic fallbacks.\n */\n\nimport type { BrandConfig } from '../types';\n\n// ---------------------------------------------------------------------------\n// Hex to RGB helper\n// ---------------------------------------------------------------------------\n\n/**\n * Converts a hex color string to an \"R, G, B\" string for use in rgb()/rgba().\n * Supports 3-digit (#abc) and 6-digit (#aabbcc) hex values.\n * Returns null for invalid inputs.\n */\nexport function hexToRgb(hex: string): string | null {\n if (!hex || typeof hex !== 'string') return null;\n\n const cleaned = hex.replace(/^#/, '');\n\n let r: number;\n let g: number;\n let b: number;\n\n if (cleaned.length === 3) {\n r = parseInt(cleaned[0] + cleaned[0], 16);\n g = parseInt(cleaned[1] + cleaned[1], 16);\n b = parseInt(cleaned[2] + cleaned[2], 16);\n } else if (cleaned.length === 6) {\n r = parseInt(cleaned.substring(0, 2), 16);\n g = parseInt(cleaned.substring(2, 4), 16);\n b = parseInt(cleaned.substring(4, 6), 16);\n } else {\n return null;\n }\n\n if (isNaN(r) || isNaN(g) || isNaN(b)) return null;\n\n return `${r}, ${g}, ${b}`;\n}\n\n// ---------------------------------------------------------------------------\n// Default theme values\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_BRAND_CONFIG: BrandConfig = {\n primary: '#6366f1',\n secondary: '#8b5cf6',\n background: '#ffffff',\n backgroundElevated: '#f8fafc',\n surface: '#f1f5f9',\n surfaceHover: '#e2e8f0',\n surfaceBorder: '#e2e8f0',\n textPrimary: '#0f172a',\n textSecondary: '#475569',\n textTertiary: '#94a3b8',\n fontHeading: \"'Inter', system-ui, -apple-system, sans-serif\",\n fontBody: \"'Inter', system-ui, -apple-system, sans-serif\",\n radius: {\n sm: '0.375rem',\n md: '0.5rem',\n lg: '0.75rem',\n },\n};\n\nexport const DEFAULT_DARK_MODE: Partial<BrandConfig> = {\n background: '#1a1a1f',\n backgroundElevated: '#222228',\n surface: '#2a2a30',\n surfaceHover: '#333338',\n surfaceBorder: '#334155',\n textPrimary: '#f8fafc',\n textSecondary: '#cbd5e1',\n textTertiary: '#64748b',\n};\n\n// ---------------------------------------------------------------------------\n// CSS variable generation\n// ---------------------------------------------------------------------------\n\nfunction buildVariables(config: BrandConfig): string {\n const primaryRgb = hexToRgb(config.primary) || '99, 102, 241';\n const secondaryRgb = hexToRgb(config.secondary) || '139, 92, 246';\n\n const radius = config.radius || DEFAULT_BRAND_CONFIG.radius!;\n\n return [\n // Primary tokens (--sk-*)\n `--sk-primary: ${config.primary};`,\n `--sk-primary-rgb: ${primaryRgb};`,\n `--sk-secondary: ${config.secondary};`,\n `--sk-secondary-rgb: ${secondaryRgb};`,\n `--sk-bg: ${config.background};`,\n `--sk-bg-elevated: ${config.backgroundElevated};`,\n `--sk-surface: ${config.surface};`,\n `--sk-surface-hover: ${config.surfaceHover};`,\n `--sk-border: ${config.surfaceBorder};`,\n `--sk-text-primary: ${config.textPrimary};`,\n `--sk-text-secondary: ${config.textSecondary};`,\n `--sk-text-tertiary: ${config.textTertiary};`,\n `--sk-radius-sm: ${radius.sm};`,\n `--sk-radius: ${radius.md};`,\n `--sk-radius-lg: ${radius.lg};`,\n `--sk-font-heading: ${config.fontHeading};`,\n `--sk-font: ${config.fontBody};`,\n ].join('\\n ');\n}\n\nfunction buildDarkVariables(base: BrandConfig): string {\n const dark = {\n ...DEFAULT_DARK_MODE,\n ...base.darkMode,\n // Primary/secondary carry over unless explicitly overridden\n primary: base.darkMode?.primary || base.primary,\n secondary: base.darkMode?.secondary || base.secondary,\n };\n\n const primaryRgb = hexToRgb(dark.primary!) || hexToRgb(base.primary) || '99, 102, 241';\n const secondaryRgb = hexToRgb(dark.secondary!) || hexToRgb(base.secondary) || '139, 92, 246';\n\n return [\n // Primary tokens (--sk-*)\n `--sk-primary: ${dark.primary};`,\n `--sk-primary-rgb: ${primaryRgb};`,\n `--sk-secondary: ${dark.secondary};`,\n `--sk-secondary-rgb: ${secondaryRgb};`,\n `--sk-bg: ${dark.background || base.background || DEFAULT_DARK_MODE.background};`,\n `--sk-bg-elevated: ${dark.backgroundElevated || base.backgroundElevated || DEFAULT_DARK_MODE.backgroundElevated};`,\n `--sk-surface: ${dark.surface || base.surface || DEFAULT_DARK_MODE.surface};`,\n `--sk-surface-hover: ${dark.surfaceHover || base.surfaceHover || DEFAULT_DARK_MODE.surfaceHover};`,\n `--sk-border: ${dark.surfaceBorder || base.surfaceBorder || DEFAULT_DARK_MODE.surfaceBorder};`,\n `--sk-text-primary: ${dark.textPrimary || base.textPrimary || DEFAULT_DARK_MODE.textPrimary};`,\n `--sk-text-secondary: ${dark.textSecondary || base.textSecondary || DEFAULT_DARK_MODE.textSecondary};`,\n `--sk-text-tertiary: ${dark.textTertiary || base.textTertiary || DEFAULT_DARK_MODE.textTertiary};`,\n ].join('\\n ');\n}\n\n/**\n * Generates a complete CSS string with all --sk-* custom properties.\n *\n * Produces:\n * - `:root` / `[data-theme=\"light\"]` block with light mode values\n * - `[data-theme=\"dark\"]` block with dark mode values\n * - `@media (prefers-color-scheme: dark)` fallback for auto detection\n *\n * Usage: inject the returned string into a `<style>` tag.\n */\nexport function generateBrandCSS(config: BrandConfig): string {\n const merged: BrandConfig = {\n ...DEFAULT_BRAND_CONFIG,\n ...config,\n radius: {\n ...DEFAULT_BRAND_CONFIG.radius!,\n ...config.radius,\n },\n };\n\n const lightVars = buildVariables(merged);\n const darkVars = buildDarkVariables(merged);\n\n return `\n:root, [data-theme=\"light\"] {\n ${lightVars}\n}\n\n[data-theme=\"dark\"] {\n ${darkVars}\n}\n\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n ${darkVars}\n }\n}\n`.trim();\n}\n\n/**\n * Merges a partial brand config with defaults, returning a complete BrandConfig.\n */\nexport function mergeBrandConfig(partial: Partial<BrandConfig>): BrandConfig {\n return {\n ...DEFAULT_BRAND_CONFIG,\n ...partial,\n radius: {\n ...DEFAULT_BRAND_CONFIG.radius!,\n ...partial.radius,\n },\n };\n}\n","/**\n * @sonordev/agency-site-kit — AgencySiteKitLayout\n *\n * Server component that wraps children with brand CSS variables.\n * Fetches the brand configuration from the Portal API and injects\n * a <style> tag with all --sk-* custom properties (plus --agency-* aliases for backwards compatibility).\n *\n * Usage in app/layout.tsx:\n *\n * ```tsx\n * import { AgencySiteKitLayout } from '@sonordev/agency-site-kit';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html lang=\"en\">\n * <body>\n * <AgencySiteKitLayout>\n * {children}\n * </AgencySiteKitLayout>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\n\nimport React from 'react';\nimport { resolveApiOrigin } from '@sonordev/site-kit/server';\nimport { apiGet } from '../shared/api';\nimport { generateBrandCSS, mergeBrandConfig } from '../shared/brand';\nimport type { BrandConfig, PortfolioConfigResponse } from '../types';\n\nexport interface AgencySiteKitLayoutProps {\n children: React.ReactNode;\n /** Override the API key (defaults to process.env.SONOR_API_KEY) */\n apiKey?: string;\n /** Override the API URL (defaults to process.env.SONOR_API_URL or https://api.sonor.io) */\n apiUrl?: string;\n /** Override brand config instead of fetching from API */\n brandConfig?: Partial<BrandConfig>;\n /** Force a specific theme: \"light\" | \"dark\". Omit for system preference. */\n theme?: 'light' | 'dark';\n /** Additional class name for the wrapper div */\n className?: string;\n /**\n * When true, screen-reader-only link to `/llms.txt` after children. Default false.\n * Prefer `buildAiDiscoveryHeaders` from `@sonordev/site-kit/llms` in root layout.\n */\n showLlmsTxtFooterLink?: boolean;\n}\n\n/**\n * Fetches brand configuration from the Portal API.\n * Returns a merged BrandConfig with defaults applied.\n * Falls back to defaults if the API call fails.\n */\nasync function fetchBrandConfig(\n apiKey?: string,\n apiUrl?: string,\n): Promise<BrandConfig> {\n const response = await apiGet<PortfolioConfigResponse>(\n '/api/public/portfolio/config',\n 3600,\n apiKey,\n apiUrl,\n );\n\n if (response?.brand) {\n return mergeBrandConfig(response.brand);\n }\n\n return mergeBrandConfig({});\n}\n\n/**\n * AgencySiteKitLayout — Server component wrapper\n *\n * Fetches the agency's brand configuration and injects CSS custom properties\n * so all child components can use --sk-* variables for theming.\n *\n * This is an async server component (RSC). It makes one API call at render\n * time, cached via Next.js ISR (default 1 hour revalidation).\n */\nexport async function AgencySiteKitLayout({\n children,\n apiKey,\n apiUrl,\n brandConfig: brandConfigOverride,\n theme,\n className,\n showLlmsTxtFooterLink = false,\n}: AgencySiteKitLayoutProps): Promise<React.ReactElement> {\n // Use override brand config if provided, otherwise fetch from API\n const brandConfig = brandConfigOverride\n ? mergeBrandConfig(brandConfigOverride)\n : await fetchBrandConfig(apiKey, apiUrl);\n\n const cssString = generateBrandCSS(brandConfig);\n\n const resolvedApiUrl =\n apiUrl ??\n process.env.SONOR_API_URL ??\n process.env.UPTRADE_API_URL ??\n 'https://api.sonor.io';\n // Origin-resolution shared with core SiteKitLayout (one source of truth).\n // The preconnect itself stays — upforge relies on AgencySiteKitLayout to emit\n // it (it doesn't mount core's SiteKitLayout).\n const preconnectHref = resolveApiOrigin(resolvedApiUrl);\n\n return React.createElement(\n React.Fragment,\n null,\n React.createElement('link', {\n rel: 'preconnect',\n href: preconnectHref,\n crossOrigin: 'anonymous',\n }),\n React.createElement('link', {\n rel: 'dns-prefetch',\n href: preconnectHref,\n }),\n React.createElement('style', {\n dangerouslySetInnerHTML: { __html: cssString },\n 'data-agency-site-kit': 'brand',\n }),\n React.createElement(\n 'div',\n {\n 'data-theme': theme || undefined,\n 'data-agency-site-kit': 'root',\n className,\n style: {\n fontFamily: 'var(--sk-font)',\n color: 'var(--sk-text-primary)',\n backgroundColor: 'var(--sk-bg)',\n },\n },\n children,\n showLlmsTxtFooterLink\n ? React.createElement(\n 'footer',\n { className: 'sr-only', 'aria-hidden': true },\n React.createElement('a', { href: '/llms.txt' }, 'llms.txt'),\n )\n : null,\n ),\n );\n}\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { resolveServerApiConfig, serverApiFetch } from '@sonordev/site-kit/server';
|
|
2
|
+
|
|
3
|
+
// src/shared/api.ts
|
|
4
|
+
var DEFAULT_REVALIDATE = 3600;
|
|
5
|
+
var LOG_PREFIX = "[@sonordev/agency-site-kit]";
|
|
6
|
+
function getApiConfig() {
|
|
7
|
+
const config = resolveServerApiConfig();
|
|
8
|
+
if (!config) {
|
|
9
|
+
console.warn(
|
|
10
|
+
`${LOG_PREFIX} Missing SONOR_API_KEY environment variable. Portfolio data will not be fetched.`
|
|
11
|
+
);
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return config;
|
|
15
|
+
}
|
|
16
|
+
async function apiFetch(path, options = {}) {
|
|
17
|
+
const {
|
|
18
|
+
revalidate = DEFAULT_REVALIDATE,
|
|
19
|
+
tags,
|
|
20
|
+
headers,
|
|
21
|
+
apiKey,
|
|
22
|
+
apiUrl,
|
|
23
|
+
method,
|
|
24
|
+
body,
|
|
25
|
+
...init
|
|
26
|
+
} = options;
|
|
27
|
+
return serverApiFetch(path, {
|
|
28
|
+
method,
|
|
29
|
+
body: body ?? void 0,
|
|
30
|
+
headers,
|
|
31
|
+
revalidate,
|
|
32
|
+
tags,
|
|
33
|
+
apiKey,
|
|
34
|
+
apiUrl,
|
|
35
|
+
retry: false,
|
|
36
|
+
requireKey: false,
|
|
37
|
+
logPrefix: LOG_PREFIX,
|
|
38
|
+
init
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function apiGet(path, revalidate, apiKey, apiUrl) {
|
|
42
|
+
return apiFetch(path, {
|
|
43
|
+
method: "GET",
|
|
44
|
+
revalidate,
|
|
45
|
+
apiKey,
|
|
46
|
+
apiUrl
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { apiFetch, apiGet, getApiConfig };
|
|
51
|
+
//# sourceMappingURL=chunk-TVFXN6W6.js.map
|
|
52
|
+
//# sourceMappingURL=chunk-TVFXN6W6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/api.ts"],"names":[],"mappings":";;;AAyBA,IAAM,kBAAA,GAAqB,IAAA;AAC3B,IAAM,UAAA,GAAa,6BAAA;AASZ,SAAS,YAAA,GAAiC;AAC/C,EAAA,MAAM,SAAS,sBAAA,EAAuB;AAEtC,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,GAAG,UAAU,CAAA,gFAAA;AAAA,KAEf;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO,MAAA;AACT;AAgCA,eAAsB,QAAA,CACpB,IAAA,EACA,OAAA,GAA2B,EAAC,EACT;AACnB,EAAA,MAAM;AAAA,IACJ,UAAA,GAAa,kBAAA;AAAA,IACb,IAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA;AAAA,IACA,GAAG;AAAA,GACL,GAAI,OAAA;AAEJ,EAAA,OAAO,eAAkB,IAAA,EAAM;AAAA,IAC7B,MAAA;AAAA,IACA,MAAM,IAAA,IAAQ,MAAA;AAAA,IACd,OAAA;AAAA,IACA,UAAA;AAAA,IACA,IAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,KAAA,EAAO,KAAA;AAAA,IACP,UAAA,EAAY,KAAA;AAAA,IACZ,SAAA,EAAW,UAAA;AAAA,IACX;AAAA,GACD,CAAA;AACH;AAUA,eAAsB,MAAA,CACpB,IAAA,EACA,UAAA,EACA,MAAA,EACA,MAAA,EACmB;AACnB,EAAA,OAAO,SAAY,IAAA,EAAM;AAAA,IACvB,MAAA,EAAQ,KAAA;AAAA,IACR,UAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH","file":"chunk-TVFXN6W6.js","sourcesContent":["/**\n * @sonordev/agency-site-kit — API fetch utilities\n *\n * Server-side only. Thin adapters over core `@sonordev/site-kit`'s shared server\n * fetcher (`@sonordev/site-kit/server`) — the single source of truth for the\n * Sonor server data plane. This package no longer ships its own `apiFetch` copy;\n * it reuses core's so the two can never drift (monorepo CLAUDE.md,\n * \"Consolidate Duplicated Logic\").\n *\n * Config resolution (SONOR_API_KEY → `x-api-key`, SONOR_API_URL, the legacy\n * UPTRADE_API_KEY fallback) and the `https://api.sonor.io` base all live in core.\n *\n * NOTE: importing this module pulls core's `server-only` guard, so it (and any\n * barrel that re-exports it, including the package root and AgencySiteKitLayout)\n * is server-only. Client components must import from\n * `@sonordev/agency-site-kit/portfolio/client`.\n */\n\nimport {\n resolveServerApiConfig,\n serverApiFetch,\n type ServerApiConfig,\n} from '@sonordev/site-kit/server';\n\n/** Portfolio fetches default to a 1h ISR window (overridable per call). */\nconst DEFAULT_REVALIDATE = 3600;\nconst LOG_PREFIX = '[@sonordev/agency-site-kit]';\n\ntype ApiConfig = ServerApiConfig;\n\n/**\n * Reads API configuration from overrides → environment (delegates to core's\n * `resolveServerApiConfig`). Returns null and warns when no API key is found, so\n * portfolio data degrades gracefully instead of throwing.\n */\nexport function getApiConfig(): ApiConfig | null {\n const config = resolveServerApiConfig();\n\n if (!config) {\n console.warn(\n `${LOG_PREFIX} Missing SONOR_API_KEY environment variable. ` +\n 'Portfolio data will not be fetched.',\n );\n return null;\n }\n\n return config;\n}\n\ninterface ApiFetchOptions extends Omit<RequestInit, 'headers'> {\n /** ISR revalidation interval in seconds. Defaults to 3600. Set to 0 for no caching. */\n revalidate?: number;\n /**\n * Next.js cache tags for on-demand invalidation. Portfolio fetches use\n * PORTFOLIO_CACHE_TAG so the Portal can bust list + detail + sitemap data\n * with a single revalidateTag call (see ./revalidate).\n */\n tags?: string[];\n /** Additional headers to merge with defaults */\n headers?: Record<string, string>;\n /** Override API key (e.g. passed from layout props) */\n apiKey?: string;\n /** Override API URL */\n apiUrl?: string;\n}\n\n/**\n * Fetch wrapper for the public portfolio API — delegates to core's\n * `serverApiFetch`.\n *\n * - Injects `x-api-key` automatically\n * - Parses JSON responses\n * - Uses Next.js ISR caching via `next.revalidate` (+ optional cache `tags`)\n * - Returns null on failure (or a missing key) instead of throwing\n *\n * Single-attempt by design — portfolio pages degrade gracefully rather than\n * hard-failing. (Core's fetcher supports retry/backoff; this preserves\n * agency-site-kit's original behavior.)\n */\nexport async function apiFetch<T>(\n path: string,\n options: ApiFetchOptions = {},\n): Promise<T | null> {\n const {\n revalidate = DEFAULT_REVALIDATE,\n tags,\n headers,\n apiKey,\n apiUrl,\n method,\n body,\n ...init\n } = options;\n\n return serverApiFetch<T>(path, {\n method,\n body: body ?? undefined,\n headers,\n revalidate,\n tags,\n apiKey,\n apiUrl,\n retry: false,\n requireKey: false,\n logPrefix: LOG_PREFIX,\n init: init as RequestInit,\n });\n}\n\n/**\n * GET shorthand for apiFetch.\n *\n * @param path API path (e.g. \"/api/public/portfolio/items\")\n * @param revalidate ISR revalidation in seconds (default 3600)\n * @param apiKey Optional override API key\n * @param apiUrl Optional override API URL\n */\nexport async function apiGet<T>(\n path: string,\n revalidate?: number,\n apiKey?: string,\n apiUrl?: string,\n): Promise<T | null> {\n return apiFetch<T>(path, {\n method: 'GET',\n revalidate,\n apiKey,\n apiUrl,\n });\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/portfolio/sanitize.ts
|
|
2
|
+
function humanizeArchPageEntry(page) {
|
|
3
|
+
const title = (page?.title || "").trim();
|
|
4
|
+
if (title && !title.startsWith("/")) return { title };
|
|
5
|
+
const source = title || (page?.path || "").trim();
|
|
6
|
+
const lastSegment = source.split(/[?#]/)[0].split("/").filter(Boolean).pop() || "";
|
|
7
|
+
const label = lastSegment.replace(/\.(html?|php|aspx?)$/i, "").replace(/[-_]+/g, " ").trim().replace(/\b\w/g, (c) => c.toUpperCase());
|
|
8
|
+
return { title: label || "Home" };
|
|
9
|
+
}
|
|
10
|
+
function sanitizeSiteArchitectureData(data) {
|
|
11
|
+
if (!data || !Array.isArray(data.groups)) return data;
|
|
12
|
+
return {
|
|
13
|
+
...data,
|
|
14
|
+
groups: data.groups.map((group) => ({
|
|
15
|
+
...group,
|
|
16
|
+
pages: Array.isArray(group.pages) ? group.pages.map(humanizeArchPageEntry) : []
|
|
17
|
+
}))
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function sanitizePortfolioSections(sections) {
|
|
21
|
+
return sections.map(
|
|
22
|
+
(section) => section?.sectionType === "portfolioSiteArchitecture" ? { ...section, data: sanitizeSiteArchitectureData(section.data) } : section
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { sanitizePortfolioSections, sanitizeSiteArchitectureData };
|
|
27
|
+
//# sourceMappingURL=chunk-VVTK4OL6.js.map
|
|
28
|
+
//# sourceMappingURL=chunk-VVTK4OL6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/portfolio/sanitize.ts"],"names":[],"mappings":";AAuBO,SAAS,sBAAsB,IAAA,EAAwC;AAC5E,EAAA,MAAM,KAAA,GAAA,CAAS,IAAA,EAAM,KAAA,IAAS,EAAA,EAAI,IAAA,EAAK;AACvC,EAAA,IAAI,KAAA,IAAS,CAAC,KAAA,CAAM,UAAA,CAAW,GAAG,CAAA,EAAG,OAAO,EAAE,KAAA,EAAM;AACpD,EAAA,MAAM,MAAA,GAAS,KAAA,IAAA,CAAU,IAAA,EAAM,IAAA,IAAQ,IAAI,IAAA,EAAK;AAChD,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,KAAA,CAAM,MAAM,EAAE,CAAC,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CAAE,KAAI,IAAK,EAAA;AAChF,EAAA,MAAM,QAAQ,WAAA,CACX,OAAA,CAAQ,yBAAyB,EAAE,CAAA,CACnC,QAAQ,QAAA,EAAU,GAAG,CAAA,CACrB,IAAA,GACA,OAAA,CAAQ,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,CAAE,aAAa,CAAA;AAC1C,EAAA,OAAO,EAAE,KAAA,EAAO,KAAA,IAAS,MAAA,EAAO;AAClC;AAGO,SAAS,6BAA6B,IAAA,EAAgB;AAC3D,EAAA,IAAI,CAAC,QAAQ,CAAC,KAAA,CAAM,QAAQ,IAAA,CAAK,MAAM,GAAG,OAAO,IAAA;AACjD,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,MAAA,EAAQ,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,MAAgB;AAAA,MACvC,GAAG,KAAA;AAAA,MACH,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,KAAK,CAAA,GAAI,KAAA,CAAM,KAAA,CAAM,GAAA,CAAI,qBAAqB,CAAA,GAAI;AAAC,KAChF,CAAE;AAAA,GACJ;AACF;AAGO,SAAS,0BACd,QAAA,EACK;AACL,EAAA,OAAO,QAAA,CAAS,GAAA;AAAA,IAAI,CAAC,OAAA,KACnB,OAAA,EAAS,WAAA,KAAgB,2BAAA,GACrB,EAAE,GAAG,OAAA,EAAS,IAAA,EAAM,4BAAA,CAA6B,OAAA,CAAQ,IAAI,GAAE,GAC/D;AAAA,GACN;AACF","file":"chunk-VVTK4OL6.js","sourcesContent":["/**\n * Portfolio section sanitization — single source of truth.\n *\n * Site-architecture entries arrive from generation with raw CLIENT-site paths\n * (often title === path, e.g. \"/wheels/amp-tires\"). Those strings must never\n * cross a server→client props boundary: Googlebot extracts URL-like strings\n * from the RSC flight payload and crawls them against the AGENCY site's\n * origin, flooding Search Console with 404s (seen on upforge.io, June 2026 —\n * ~100 404'd client paths, all traced to /work/* case-study payloads).\n *\n * The enforcement point is `getPortfolioItem` (portfolio/server.ts): tsup's\n * \"add 'use client' to chunks with hooks\" post-processing turns PortfolioPage\n * into a client component in the published build, so sanitizing at render\n * time is too late — the full `item` prop is already serialized. The\n * PortfolioPage call remains as defense-in-depth for a future true-RSC build.\n */\n\ninterface ArchPageEntry {\n path?: string;\n title?: string;\n}\n\n/** Convert a raw path/title entry into a display-only { title } label. */\nexport function humanizeArchPageEntry(page: ArchPageEntry): { title: string } {\n const title = (page?.title || '').trim();\n if (title && !title.startsWith('/')) return { title };\n const source = title || (page?.path || '').trim();\n const lastSegment = source.split(/[?#]/)[0].split('/').filter(Boolean).pop() || '';\n const label = lastSegment\n .replace(/\\.(html?|php|aspx?)$/i, '')\n .replace(/[-_]+/g, ' ')\n .trim()\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n return { title: label || 'Home' };\n}\n\n/** Strip raw page paths from a portfolioSiteArchitecture data payload. */\nexport function sanitizeSiteArchitectureData(data: any): any {\n if (!data || !Array.isArray(data.groups)) return data;\n return {\n ...data,\n groups: data.groups.map((group: any) => ({\n ...group,\n pages: Array.isArray(group.pages) ? group.pages.map(humanizeArchPageEntry) : [],\n })),\n };\n}\n\n/** Sanitize every section that can carry raw client-site paths. */\nexport function sanitizePortfolioSections<T extends { sectionType?: string; data?: any }>(\n sections: T[],\n): T[] {\n return sections.map((section) =>\n section?.sectionType === 'portfolioSiteArchitecture'\n ? { ...section, data: sanitizeSiteArchitectureData(section.data) }\n : section,\n );\n}\n"]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkEE75WRCM_cjs = require('./chunk-EE75WRCM.cjs');
|
|
4
4
|
var React = require('react');
|
|
5
|
+
var server = require('@sonordev/site-kit/server');
|
|
5
6
|
|
|
6
7
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
8
|
|
|
@@ -147,15 +148,8 @@ function mergeBrandConfig(partial) {
|
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
// src/layout/AgencySiteKitLayout.tsx
|
|
150
|
-
function portalApiOrigin(apiUrl) {
|
|
151
|
-
try {
|
|
152
|
-
return new URL(apiUrl.replace(/\/$/, "")).origin;
|
|
153
|
-
} catch {
|
|
154
|
-
return "https://api.sonor.io";
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
151
|
async function fetchBrandConfig(apiKey, apiUrl) {
|
|
158
|
-
const response = await
|
|
152
|
+
const response = await chunkEE75WRCM_cjs.apiGet(
|
|
159
153
|
"/api/public/portfolio/config",
|
|
160
154
|
3600,
|
|
161
155
|
apiKey,
|
|
@@ -178,7 +172,7 @@ async function AgencySiteKitLayout({
|
|
|
178
172
|
const brandConfig = brandConfigOverride ? mergeBrandConfig(brandConfigOverride) : await fetchBrandConfig(apiKey, apiUrl);
|
|
179
173
|
const cssString = generateBrandCSS(brandConfig);
|
|
180
174
|
const resolvedApiUrl = apiUrl ?? process.env.SONOR_API_URL ?? process.env.UPTRADE_API_URL ?? "https://api.sonor.io";
|
|
181
|
-
const preconnectHref =
|
|
175
|
+
const preconnectHref = server.resolveApiOrigin(resolvedApiUrl);
|
|
182
176
|
return React__default.default.createElement(
|
|
183
177
|
React__default.default.Fragment,
|
|
184
178
|
null,
|
|
@@ -223,5 +217,5 @@ exports.DEFAULT_DARK_MODE = DEFAULT_DARK_MODE;
|
|
|
223
217
|
exports.generateBrandCSS = generateBrandCSS;
|
|
224
218
|
exports.hexToRgb = hexToRgb;
|
|
225
219
|
exports.mergeBrandConfig = mergeBrandConfig;
|
|
226
|
-
//# sourceMappingURL=chunk-
|
|
227
|
-
//# sourceMappingURL=chunk-
|
|
220
|
+
//# sourceMappingURL=chunk-WNR4KNG4.cjs.map
|
|
221
|
+
//# sourceMappingURL=chunk-WNR4KNG4.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/brand.ts","../src/layout/AgencySiteKitLayout.tsx"],"names":["apiGet","resolveApiOrigin","React"],"mappings":"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","file":"chunk-WNR4KNG4.cjs","sourcesContent":["/**\n * @sonordev/agency-site-kit — Brand customization system\n *\n * Generates CSS custom properties from a BrandConfig object.\n * Supports light/dark mode with automatic fallbacks.\n */\n\nimport type { BrandConfig } from '../types';\n\n// ---------------------------------------------------------------------------\n// Hex to RGB helper\n// ---------------------------------------------------------------------------\n\n/**\n * Converts a hex color string to an \"R, G, B\" string for use in rgb()/rgba().\n * Supports 3-digit (#abc) and 6-digit (#aabbcc) hex values.\n * Returns null for invalid inputs.\n */\nexport function hexToRgb(hex: string): string | null {\n if (!hex || typeof hex !== 'string') return null;\n\n const cleaned = hex.replace(/^#/, '');\n\n let r: number;\n let g: number;\n let b: number;\n\n if (cleaned.length === 3) {\n r = parseInt(cleaned[0] + cleaned[0], 16);\n g = parseInt(cleaned[1] + cleaned[1], 16);\n b = parseInt(cleaned[2] + cleaned[2], 16);\n } else if (cleaned.length === 6) {\n r = parseInt(cleaned.substring(0, 2), 16);\n g = parseInt(cleaned.substring(2, 4), 16);\n b = parseInt(cleaned.substring(4, 6), 16);\n } else {\n return null;\n }\n\n if (isNaN(r) || isNaN(g) || isNaN(b)) return null;\n\n return `${r}, ${g}, ${b}`;\n}\n\n// ---------------------------------------------------------------------------\n// Default theme values\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_BRAND_CONFIG: BrandConfig = {\n primary: '#6366f1',\n secondary: '#8b5cf6',\n background: '#ffffff',\n backgroundElevated: '#f8fafc',\n surface: '#f1f5f9',\n surfaceHover: '#e2e8f0',\n surfaceBorder: '#e2e8f0',\n textPrimary: '#0f172a',\n textSecondary: '#475569',\n textTertiary: '#94a3b8',\n fontHeading: \"'Inter', system-ui, -apple-system, sans-serif\",\n fontBody: \"'Inter', system-ui, -apple-system, sans-serif\",\n radius: {\n sm: '0.375rem',\n md: '0.5rem',\n lg: '0.75rem',\n },\n};\n\nexport const DEFAULT_DARK_MODE: Partial<BrandConfig> = {\n background: '#1a1a1f',\n backgroundElevated: '#222228',\n surface: '#2a2a30',\n surfaceHover: '#333338',\n surfaceBorder: '#334155',\n textPrimary: '#f8fafc',\n textSecondary: '#cbd5e1',\n textTertiary: '#64748b',\n};\n\n// ---------------------------------------------------------------------------\n// CSS variable generation\n// ---------------------------------------------------------------------------\n\nfunction buildVariables(config: BrandConfig): string {\n const primaryRgb = hexToRgb(config.primary) || '99, 102, 241';\n const secondaryRgb = hexToRgb(config.secondary) || '139, 92, 246';\n\n const radius = config.radius || DEFAULT_BRAND_CONFIG.radius!;\n\n return [\n // Primary tokens (--sk-*)\n `--sk-primary: ${config.primary};`,\n `--sk-primary-rgb: ${primaryRgb};`,\n `--sk-secondary: ${config.secondary};`,\n `--sk-secondary-rgb: ${secondaryRgb};`,\n `--sk-bg: ${config.background};`,\n `--sk-bg-elevated: ${config.backgroundElevated};`,\n `--sk-surface: ${config.surface};`,\n `--sk-surface-hover: ${config.surfaceHover};`,\n `--sk-border: ${config.surfaceBorder};`,\n `--sk-text-primary: ${config.textPrimary};`,\n `--sk-text-secondary: ${config.textSecondary};`,\n `--sk-text-tertiary: ${config.textTertiary};`,\n `--sk-radius-sm: ${radius.sm};`,\n `--sk-radius: ${radius.md};`,\n `--sk-radius-lg: ${radius.lg};`,\n `--sk-font-heading: ${config.fontHeading};`,\n `--sk-font: ${config.fontBody};`,\n ].join('\\n ');\n}\n\nfunction buildDarkVariables(base: BrandConfig): string {\n const dark = {\n ...DEFAULT_DARK_MODE,\n ...base.darkMode,\n // Primary/secondary carry over unless explicitly overridden\n primary: base.darkMode?.primary || base.primary,\n secondary: base.darkMode?.secondary || base.secondary,\n };\n\n const primaryRgb = hexToRgb(dark.primary!) || hexToRgb(base.primary) || '99, 102, 241';\n const secondaryRgb = hexToRgb(dark.secondary!) || hexToRgb(base.secondary) || '139, 92, 246';\n\n return [\n // Primary tokens (--sk-*)\n `--sk-primary: ${dark.primary};`,\n `--sk-primary-rgb: ${primaryRgb};`,\n `--sk-secondary: ${dark.secondary};`,\n `--sk-secondary-rgb: ${secondaryRgb};`,\n `--sk-bg: ${dark.background || base.background || DEFAULT_DARK_MODE.background};`,\n `--sk-bg-elevated: ${dark.backgroundElevated || base.backgroundElevated || DEFAULT_DARK_MODE.backgroundElevated};`,\n `--sk-surface: ${dark.surface || base.surface || DEFAULT_DARK_MODE.surface};`,\n `--sk-surface-hover: ${dark.surfaceHover || base.surfaceHover || DEFAULT_DARK_MODE.surfaceHover};`,\n `--sk-border: ${dark.surfaceBorder || base.surfaceBorder || DEFAULT_DARK_MODE.surfaceBorder};`,\n `--sk-text-primary: ${dark.textPrimary || base.textPrimary || DEFAULT_DARK_MODE.textPrimary};`,\n `--sk-text-secondary: ${dark.textSecondary || base.textSecondary || DEFAULT_DARK_MODE.textSecondary};`,\n `--sk-text-tertiary: ${dark.textTertiary || base.textTertiary || DEFAULT_DARK_MODE.textTertiary};`,\n ].join('\\n ');\n}\n\n/**\n * Generates a complete CSS string with all --sk-* custom properties.\n *\n * Produces:\n * - `:root` / `[data-theme=\"light\"]` block with light mode values\n * - `[data-theme=\"dark\"]` block with dark mode values\n * - `@media (prefers-color-scheme: dark)` fallback for auto detection\n *\n * Usage: inject the returned string into a `<style>` tag.\n */\nexport function generateBrandCSS(config: BrandConfig): string {\n const merged: BrandConfig = {\n ...DEFAULT_BRAND_CONFIG,\n ...config,\n radius: {\n ...DEFAULT_BRAND_CONFIG.radius!,\n ...config.radius,\n },\n };\n\n const lightVars = buildVariables(merged);\n const darkVars = buildDarkVariables(merged);\n\n return `\n:root, [data-theme=\"light\"] {\n ${lightVars}\n}\n\n[data-theme=\"dark\"] {\n ${darkVars}\n}\n\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n ${darkVars}\n }\n}\n`.trim();\n}\n\n/**\n * Merges a partial brand config with defaults, returning a complete BrandConfig.\n */\nexport function mergeBrandConfig(partial: Partial<BrandConfig>): BrandConfig {\n return {\n ...DEFAULT_BRAND_CONFIG,\n ...partial,\n radius: {\n ...DEFAULT_BRAND_CONFIG.radius!,\n ...partial.radius,\n },\n };\n}\n","/**\n * @sonordev/agency-site-kit — AgencySiteKitLayout\n *\n * Server component that wraps children with brand CSS variables.\n * Fetches the brand configuration from the Portal API and injects\n * a <style> tag with all --sk-* custom properties (plus --agency-* aliases for backwards compatibility).\n *\n * Usage in app/layout.tsx:\n *\n * ```tsx\n * import { AgencySiteKitLayout } from '@sonordev/agency-site-kit';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html lang=\"en\">\n * <body>\n * <AgencySiteKitLayout>\n * {children}\n * </AgencySiteKitLayout>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\n\nimport React from 'react';\nimport { resolveApiOrigin } from '@sonordev/site-kit/server';\nimport { apiGet } from '../shared/api';\nimport { generateBrandCSS, mergeBrandConfig } from '../shared/brand';\nimport type { BrandConfig, PortfolioConfigResponse } from '../types';\n\nexport interface AgencySiteKitLayoutProps {\n children: React.ReactNode;\n /** Override the API key (defaults to process.env.SONOR_API_KEY) */\n apiKey?: string;\n /** Override the API URL (defaults to process.env.SONOR_API_URL or https://api.sonor.io) */\n apiUrl?: string;\n /** Override brand config instead of fetching from API */\n brandConfig?: Partial<BrandConfig>;\n /** Force a specific theme: \"light\" | \"dark\". Omit for system preference. */\n theme?: 'light' | 'dark';\n /** Additional class name for the wrapper div */\n className?: string;\n /**\n * When true, screen-reader-only link to `/llms.txt` after children. Default false.\n * Prefer `buildAiDiscoveryHeaders` from `@sonordev/site-kit/llms` in root layout.\n */\n showLlmsTxtFooterLink?: boolean;\n}\n\n/**\n * Fetches brand configuration from the Portal API.\n * Returns a merged BrandConfig with defaults applied.\n * Falls back to defaults if the API call fails.\n */\nasync function fetchBrandConfig(\n apiKey?: string,\n apiUrl?: string,\n): Promise<BrandConfig> {\n const response = await apiGet<PortfolioConfigResponse>(\n '/api/public/portfolio/config',\n 3600,\n apiKey,\n apiUrl,\n );\n\n if (response?.brand) {\n return mergeBrandConfig(response.brand);\n }\n\n return mergeBrandConfig({});\n}\n\n/**\n * AgencySiteKitLayout — Server component wrapper\n *\n * Fetches the agency's brand configuration and injects CSS custom properties\n * so all child components can use --sk-* variables for theming.\n *\n * This is an async server component (RSC). It makes one API call at render\n * time, cached via Next.js ISR (default 1 hour revalidation).\n */\nexport async function AgencySiteKitLayout({\n children,\n apiKey,\n apiUrl,\n brandConfig: brandConfigOverride,\n theme,\n className,\n showLlmsTxtFooterLink = false,\n}: AgencySiteKitLayoutProps): Promise<React.ReactElement> {\n // Use override brand config if provided, otherwise fetch from API\n const brandConfig = brandConfigOverride\n ? mergeBrandConfig(brandConfigOverride)\n : await fetchBrandConfig(apiKey, apiUrl);\n\n const cssString = generateBrandCSS(brandConfig);\n\n const resolvedApiUrl =\n apiUrl ??\n process.env.SONOR_API_URL ??\n process.env.UPTRADE_API_URL ??\n 'https://api.sonor.io';\n // Origin-resolution shared with core SiteKitLayout (one source of truth).\n // The preconnect itself stays — upforge relies on AgencySiteKitLayout to emit\n // it (it doesn't mount core's SiteKitLayout).\n const preconnectHref = resolveApiOrigin(resolvedApiUrl);\n\n return React.createElement(\n React.Fragment,\n null,\n React.createElement('link', {\n rel: 'preconnect',\n href: preconnectHref,\n crossOrigin: 'anonymous',\n }),\n React.createElement('link', {\n rel: 'dns-prefetch',\n href: preconnectHref,\n }),\n React.createElement('style', {\n dangerouslySetInnerHTML: { __html: cssString },\n 'data-agency-site-kit': 'brand',\n }),\n React.createElement(\n 'div',\n {\n 'data-theme': theme || undefined,\n 'data-agency-site-kit': 'root',\n className,\n style: {\n fontFamily: 'var(--sk-font)',\n color: 'var(--sk-text-primary)',\n backgroundColor: 'var(--sk-bg)',\n },\n },\n children,\n showLlmsTxtFooterLink\n ? React.createElement(\n 'footer',\n { className: 'sr-only', 'aria-hidden': true },\n React.createElement('a', { href: '/llms.txt' }, 'llms.txt'),\n )\n : null,\n ),\n );\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkAVCH4LMG_cjs = require('./chunk-AVCH4LMG.cjs');
|
|
4
4
|
require('./chunk-XONXEFJY.cjs');
|
|
5
|
+
require('./chunk-633UMQQH.cjs');
|
|
5
6
|
var chunkXQNJED46_cjs = require('./chunk-XQNJED46.cjs');
|
|
6
7
|
var chunkOA5ZM4OA_cjs = require('./chunk-OA5ZM4OA.cjs');
|
|
7
|
-
var
|
|
8
|
-
var
|
|
8
|
+
var chunkWNR4KNG4_cjs = require('./chunk-WNR4KNG4.cjs');
|
|
9
|
+
var chunkEE75WRCM_cjs = require('./chunk-EE75WRCM.cjs');
|
|
9
10
|
require('./chunk-KEOHORIH.cjs');
|
|
10
11
|
require('./chunk-IKBK7HYX.cjs');
|
|
11
12
|
var React = require('react');
|
|
@@ -200,11 +201,11 @@ function Icon({
|
|
|
200
201
|
|
|
201
202
|
Object.defineProperty(exports, "PORTFOLIO_SECTION_TYPES", {
|
|
202
203
|
enumerable: true,
|
|
203
|
-
get: function () { return
|
|
204
|
+
get: function () { return chunkAVCH4LMG_cjs.PORTFOLIO_SECTION_TYPES; }
|
|
204
205
|
});
|
|
205
206
|
Object.defineProperty(exports, "PortfolioPage", {
|
|
206
207
|
enumerable: true,
|
|
207
|
-
get: function () { return
|
|
208
|
+
get: function () { return chunkAVCH4LMG_cjs.PortfolioPage; }
|
|
208
209
|
});
|
|
209
210
|
Object.defineProperty(exports, "getRegisteredSectionTypes", {
|
|
210
211
|
enumerable: true,
|
|
@@ -228,39 +229,39 @@ Object.defineProperty(exports, "PortfolioGrid", {
|
|
|
228
229
|
});
|
|
229
230
|
Object.defineProperty(exports, "AgencySiteKitLayout", {
|
|
230
231
|
enumerable: true,
|
|
231
|
-
get: function () { return
|
|
232
|
+
get: function () { return chunkWNR4KNG4_cjs.AgencySiteKitLayout; }
|
|
232
233
|
});
|
|
233
234
|
Object.defineProperty(exports, "DEFAULT_BRAND_CONFIG", {
|
|
234
235
|
enumerable: true,
|
|
235
|
-
get: function () { return
|
|
236
|
+
get: function () { return chunkWNR4KNG4_cjs.DEFAULT_BRAND_CONFIG; }
|
|
236
237
|
});
|
|
237
238
|
Object.defineProperty(exports, "DEFAULT_DARK_MODE", {
|
|
238
239
|
enumerable: true,
|
|
239
|
-
get: function () { return
|
|
240
|
+
get: function () { return chunkWNR4KNG4_cjs.DEFAULT_DARK_MODE; }
|
|
240
241
|
});
|
|
241
242
|
Object.defineProperty(exports, "generateBrandCSS", {
|
|
242
243
|
enumerable: true,
|
|
243
|
-
get: function () { return
|
|
244
|
+
get: function () { return chunkWNR4KNG4_cjs.generateBrandCSS; }
|
|
244
245
|
});
|
|
245
246
|
Object.defineProperty(exports, "hexToRgb", {
|
|
246
247
|
enumerable: true,
|
|
247
|
-
get: function () { return
|
|
248
|
+
get: function () { return chunkWNR4KNG4_cjs.hexToRgb; }
|
|
248
249
|
});
|
|
249
250
|
Object.defineProperty(exports, "mergeBrandConfig", {
|
|
250
251
|
enumerable: true,
|
|
251
|
-
get: function () { return
|
|
252
|
+
get: function () { return chunkWNR4KNG4_cjs.mergeBrandConfig; }
|
|
252
253
|
});
|
|
253
254
|
Object.defineProperty(exports, "apiFetch", {
|
|
254
255
|
enumerable: true,
|
|
255
|
-
get: function () { return
|
|
256
|
+
get: function () { return chunkEE75WRCM_cjs.apiFetch; }
|
|
256
257
|
});
|
|
257
258
|
Object.defineProperty(exports, "apiGet", {
|
|
258
259
|
enumerable: true,
|
|
259
|
-
get: function () { return
|
|
260
|
+
get: function () { return chunkEE75WRCM_cjs.apiGet; }
|
|
260
261
|
});
|
|
261
262
|
Object.defineProperty(exports, "getApiConfig", {
|
|
262
263
|
enumerable: true,
|
|
263
|
-
get: function () { return
|
|
264
|
+
get: function () { return chunkEE75WRCM_cjs.getApiConfig; }
|
|
264
265
|
});
|
|
265
266
|
exports.Icon = Icon;
|
|
266
267
|
exports.getAvailableIcons = getAvailableIcons;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/icons.ts"],"names":["React"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @sonordev/agency-site-kit — Icon registry\n *\n * Lightweight icon system that maps string names to SVG path data.\n * No external icon library dependency — keeps the bundle small.\n * Icons are rendered as inline SVGs.\n */\n\nimport React from 'react';\n\ninterface IconDefinition {\n viewBox: string;\n paths: string[];\n /** Optional fill-rule for complex paths */\n fillRule?: 'evenodd' | 'nonzero';\n}\n\n/**\n * Icon registry — 24x24 viewBox icons (matching Lucide/Heroicons conventions).\n * All paths use stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\".\n */\nconst ICON_REGISTRY: Record<string, IconDefinition> = {\n 'arrow-up': {\n viewBox: '0 0 24 24',\n paths: ['M12 19V5', 'M5 12l7-7 7 7'],\n },\n 'arrow-down': {\n viewBox: '0 0 24 24',\n paths: ['M12 5v14', 'M19 12l-7 7-7-7'],\n },\n 'chart-bar': {\n viewBox: '0 0 24 24',\n paths: ['M12 20V10', 'M18 20V4', 'M6 20v-4'],\n },\n check: {\n viewBox: '0 0 24 24',\n paths: ['M20 6L9 17l-5-5'],\n },\n clock: {\n viewBox: '0 0 24 24',\n paths: ['M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z', 'M12 6v6l4 2'],\n },\n code: {\n viewBox: '0 0 24 24',\n paths: ['M16 18l6-6-6-6', 'M8 6l-6 6 6 6'],\n },\n globe: {\n viewBox: '0 0 24 24',\n paths: [\n 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z',\n 'M2 12h20',\n 'M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10A15.3 15.3 0 0 1 12 2z',\n ],\n },\n layout: {\n viewBox: '0 0 24 24',\n paths: [\n 'M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z',\n 'M3 9h18',\n 'M9 21V9',\n ],\n },\n lightning: {\n viewBox: '0 0 24 24',\n paths: ['M13 2L3 14h9l-1 8 10-12h-9l1-8'],\n },\n link: {\n viewBox: '0 0 24 24',\n paths: [\n 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71',\n 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71',\n ],\n },\n lock: {\n viewBox: '0 0 24 24',\n paths: [\n 'M19 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2z',\n 'M7 11V7a5 5 0 0 1 10 0v4',\n ],\n },\n mail: {\n viewBox: '0 0 24 24',\n paths: [\n 'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z',\n 'M22 6l-10 7L2 6',\n ],\n },\n monitor: {\n viewBox: '0 0 24 24',\n paths: [\n 'M20 3H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z',\n 'M8 21h8',\n 'M12 17v4',\n ],\n },\n palette: {\n viewBox: '0 0 24 24',\n paths: [\n 'M12 2C6.49 2 2 6.49 2 12s4.49 10 10 10a2 2 0 0 0 2-2v-.09a1.65 1.65 0 0 1 .92-1.48A1.65 1.65 0 0 1 16.5 19H19a2 2 0 0 0 2-2 10 10 0 0 0-9-15z',\n 'M6.5 11.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z',\n 'M10 7.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z',\n 'M14 7.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z',\n 'M17.5 11.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z',\n ],\n },\n rocket: {\n viewBox: '0 0 24 24',\n paths: [\n 'M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z',\n 'M12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z',\n 'M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0',\n 'M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5',\n ],\n },\n search: {\n viewBox: '0 0 24 24',\n paths: [\n 'M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z',\n 'M21 21l-4.35-4.35',\n ],\n },\n settings: {\n viewBox: '0 0 24 24',\n paths: [\n 'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z',\n 'M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1.08-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1.08 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9c.26.604.852.997 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1.08z',\n ],\n },\n shield: {\n viewBox: '0 0 24 24',\n paths: ['M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z'],\n },\n star: {\n viewBox: '0 0 24 24',\n paths: [\n 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z',\n ],\n },\n target: {\n viewBox: '0 0 24 24',\n paths: [\n 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z',\n 'M12 18a6 6 0 1 0 0-12 6 6 0 0 0 0 12z',\n 'M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4z',\n ],\n },\n 'trending-up': {\n viewBox: '0 0 24 24',\n paths: ['M23 6l-9.5 9.5-5-5L1 18', 'M17 6h6v6'],\n },\n users: {\n viewBox: '0 0 24 24',\n paths: [\n 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2',\n 'M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z',\n 'M23 21v-2a4 4 0 0 0-3-3.87',\n 'M16 3.13a4 4 0 0 1 0 7.75',\n ],\n },\n zap: {\n viewBox: '0 0 24 24',\n paths: ['M13 2L3 14h9l-1 8 10-12h-9l1-8'],\n },\n};\n\n/**\n * Returns the icon definition (viewBox + paths) for a given icon name.\n * Returns null if the icon is not found.\n */\nexport function getIconDefinition(name: string): IconDefinition | null {\n return ICON_REGISTRY[name] ?? null;\n}\n\n/**\n * Returns just the SVG path strings for a given icon name.\n * Useful when building custom SVG elements.\n */\nexport function getIconPaths(name: string): string[] | null {\n const def = ICON_REGISTRY[name];\n return def ? def.paths : null;\n}\n\n/**\n * Returns a list of all available icon names.\n */\nexport function getAvailableIcons(): string[] {\n return Object.keys(ICON_REGISTRY);\n}\n\n/**\n * Renders an icon as a React SVG element.\n *\n * @param name Icon name from the registry\n * @param size Width/height in pixels (default 24)\n * @param className Optional CSS class name\n * @returns React SVG element, or null if icon not found\n */\nexport function renderIcon(\n name: string,\n size: number = 24,\n className?: string,\n): React.ReactElement | null {\n const def = ICON_REGISTRY[name];\n if (!def) return null;\n\n return React.createElement(\n 'svg',\n {\n xmlns: 'http://www.w3.org/2000/svg',\n width: size,\n height: size,\n viewBox: def.viewBox,\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round' as const,\n strokeLinejoin: 'round' as const,\n className,\n 'aria-hidden': 'true',\n },\n ...def.paths.map((d, i) =>\n React.createElement('path', { key: i, d }),\n ),\n );\n}\n\n/**\n * Icon component for use in JSX.\n *\n * Usage:\n * ```tsx\n * <Icon name=\"trending-up\" size={20} className=\"text-green-500\" />\n * ```\n */\nexport function Icon({\n name,\n size = 24,\n className,\n}: {\n name: string;\n size?: number;\n className?: string;\n}): React.ReactElement | null {\n return renderIcon(name, size, className);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/shared/icons.ts"],"names":["React"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @sonordev/agency-site-kit — Icon registry\n *\n * Lightweight icon system that maps string names to SVG path data.\n * No external icon library dependency — keeps the bundle small.\n * Icons are rendered as inline SVGs.\n */\n\nimport React from 'react';\n\ninterface IconDefinition {\n viewBox: string;\n paths: string[];\n /** Optional fill-rule for complex paths */\n fillRule?: 'evenodd' | 'nonzero';\n}\n\n/**\n * Icon registry — 24x24 viewBox icons (matching Lucide/Heroicons conventions).\n * All paths use stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\".\n */\nconst ICON_REGISTRY: Record<string, IconDefinition> = {\n 'arrow-up': {\n viewBox: '0 0 24 24',\n paths: ['M12 19V5', 'M5 12l7-7 7 7'],\n },\n 'arrow-down': {\n viewBox: '0 0 24 24',\n paths: ['M12 5v14', 'M19 12l-7 7-7-7'],\n },\n 'chart-bar': {\n viewBox: '0 0 24 24',\n paths: ['M12 20V10', 'M18 20V4', 'M6 20v-4'],\n },\n check: {\n viewBox: '0 0 24 24',\n paths: ['M20 6L9 17l-5-5'],\n },\n clock: {\n viewBox: '0 0 24 24',\n paths: ['M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z', 'M12 6v6l4 2'],\n },\n code: {\n viewBox: '0 0 24 24',\n paths: ['M16 18l6-6-6-6', 'M8 6l-6 6 6 6'],\n },\n globe: {\n viewBox: '0 0 24 24',\n paths: [\n 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z',\n 'M2 12h20',\n 'M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10A15.3 15.3 0 0 1 12 2z',\n ],\n },\n layout: {\n viewBox: '0 0 24 24',\n paths: [\n 'M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z',\n 'M3 9h18',\n 'M9 21V9',\n ],\n },\n lightning: {\n viewBox: '0 0 24 24',\n paths: ['M13 2L3 14h9l-1 8 10-12h-9l1-8'],\n },\n link: {\n viewBox: '0 0 24 24',\n paths: [\n 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71',\n 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71',\n ],\n },\n lock: {\n viewBox: '0 0 24 24',\n paths: [\n 'M19 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2z',\n 'M7 11V7a5 5 0 0 1 10 0v4',\n ],\n },\n mail: {\n viewBox: '0 0 24 24',\n paths: [\n 'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z',\n 'M22 6l-10 7L2 6',\n ],\n },\n monitor: {\n viewBox: '0 0 24 24',\n paths: [\n 'M20 3H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z',\n 'M8 21h8',\n 'M12 17v4',\n ],\n },\n palette: {\n viewBox: '0 0 24 24',\n paths: [\n 'M12 2C6.49 2 2 6.49 2 12s4.49 10 10 10a2 2 0 0 0 2-2v-.09a1.65 1.65 0 0 1 .92-1.48A1.65 1.65 0 0 1 16.5 19H19a2 2 0 0 0 2-2 10 10 0 0 0-9-15z',\n 'M6.5 11.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z',\n 'M10 7.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z',\n 'M14 7.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z',\n 'M17.5 11.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z',\n ],\n },\n rocket: {\n viewBox: '0 0 24 24',\n paths: [\n 'M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z',\n 'M12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z',\n 'M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0',\n 'M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5',\n ],\n },\n search: {\n viewBox: '0 0 24 24',\n paths: [\n 'M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z',\n 'M21 21l-4.35-4.35',\n ],\n },\n settings: {\n viewBox: '0 0 24 24',\n paths: [\n 'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z',\n 'M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1.08-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1.08 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9c.26.604.852.997 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1.08z',\n ],\n },\n shield: {\n viewBox: '0 0 24 24',\n paths: ['M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z'],\n },\n star: {\n viewBox: '0 0 24 24',\n paths: [\n 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z',\n ],\n },\n target: {\n viewBox: '0 0 24 24',\n paths: [\n 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z',\n 'M12 18a6 6 0 1 0 0-12 6 6 0 0 0 0 12z',\n 'M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4z',\n ],\n },\n 'trending-up': {\n viewBox: '0 0 24 24',\n paths: ['M23 6l-9.5 9.5-5-5L1 18', 'M17 6h6v6'],\n },\n users: {\n viewBox: '0 0 24 24',\n paths: [\n 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2',\n 'M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z',\n 'M23 21v-2a4 4 0 0 0-3-3.87',\n 'M16 3.13a4 4 0 0 1 0 7.75',\n ],\n },\n zap: {\n viewBox: '0 0 24 24',\n paths: ['M13 2L3 14h9l-1 8 10-12h-9l1-8'],\n },\n};\n\n/**\n * Returns the icon definition (viewBox + paths) for a given icon name.\n * Returns null if the icon is not found.\n */\nexport function getIconDefinition(name: string): IconDefinition | null {\n return ICON_REGISTRY[name] ?? null;\n}\n\n/**\n * Returns just the SVG path strings for a given icon name.\n * Useful when building custom SVG elements.\n */\nexport function getIconPaths(name: string): string[] | null {\n const def = ICON_REGISTRY[name];\n return def ? def.paths : null;\n}\n\n/**\n * Returns a list of all available icon names.\n */\nexport function getAvailableIcons(): string[] {\n return Object.keys(ICON_REGISTRY);\n}\n\n/**\n * Renders an icon as a React SVG element.\n *\n * @param name Icon name from the registry\n * @param size Width/height in pixels (default 24)\n * @param className Optional CSS class name\n * @returns React SVG element, or null if icon not found\n */\nexport function renderIcon(\n name: string,\n size: number = 24,\n className?: string,\n): React.ReactElement | null {\n const def = ICON_REGISTRY[name];\n if (!def) return null;\n\n return React.createElement(\n 'svg',\n {\n xmlns: 'http://www.w3.org/2000/svg',\n width: size,\n height: size,\n viewBox: def.viewBox,\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round' as const,\n strokeLinejoin: 'round' as const,\n className,\n 'aria-hidden': 'true',\n },\n ...def.paths.map((d, i) =>\n React.createElement('path', { key: i, d }),\n ),\n );\n}\n\n/**\n * Icon component for use in JSX.\n *\n * Usage:\n * ```tsx\n * <Icon name=\"trending-up\" size={20} className=\"text-green-500\" />\n * ```\n */\nexport function Icon({\n name,\n size = 24,\n className,\n}: {\n name: string;\n size?: number;\n className?: string;\n}): React.ReactElement | null {\n return renderIcon(name, size, className);\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { B as BrandConfig } from './types-DL4t_Cfa.cjs';
|
|
2
2
|
export { M as MetricSource, e as MetricsDelta, f as MetricsSnapshot, g as PORTFOLIO_SECTION_TYPES, h as PortfolioAnnotation, i as PortfolioBeforeAfterData, j as PortfolioCTAData, k as PortfolioChallengeItem, l as PortfolioChallengesData, m as PortfolioConfigResponse, n as PortfolioConversionFunnelData, o as PortfolioDetailsData, p as PortfolioFeatureSpotlightData, q as PortfolioFunnelStage, r as PortfolioGalleryData, s as PortfolioGalleryImage, t as PortfolioHeroData, u as PortfolioItem, P as PortfolioItemFull, v as PortfolioKPI, a as PortfolioListResponse, w as PortfolioMetricsDataPoint, x as PortfolioMetricsTimelineData, y as PortfolioResultItem, z as PortfolioResultsData, A as PortfolioSection, d as PortfolioSectionData, c as PortfolioSectionDataMap, b as PortfolioSectionType, C as PortfolioSeoData, D as PortfolioServiceItem, E as PortfolioServicesData, F as PortfolioStrategyData, G as PortfolioStrategyPhase, H as PortfolioTeamData, I as PortfolioTeamMember, J as PortfolioTechItem, K as PortfolioTechStackData, L as PortfolioTestimonialData, N as PortfolioVideoData, S as SanityImageRef } from './types-DL4t_Cfa.cjs';
|
|
3
|
+
import { ServerApiConfig } from '@sonordev/site-kit/server';
|
|
3
4
|
import React from 'react';
|
|
4
5
|
export { AgencySiteKitLayout } from './layout/index.cjs';
|
|
5
6
|
export { PortfolioPage } from './portfolio/index.cjs';
|
|
@@ -41,17 +42,26 @@ declare function mergeBrandConfig(partial: Partial<BrandConfig>): BrandConfig;
|
|
|
41
42
|
/**
|
|
42
43
|
* @sonordev/agency-site-kit — API fetch utilities
|
|
43
44
|
*
|
|
44
|
-
* Server-side only.
|
|
45
|
-
*
|
|
45
|
+
* Server-side only. Thin adapters over core `@sonordev/site-kit`'s shared server
|
|
46
|
+
* fetcher (`@sonordev/site-kit/server`) — the single source of truth for the
|
|
47
|
+
* Sonor server data plane. This package no longer ships its own `apiFetch` copy;
|
|
48
|
+
* it reuses core's so the two can never drift (monorepo CLAUDE.md,
|
|
49
|
+
* "Consolidate Duplicated Logic").
|
|
50
|
+
*
|
|
51
|
+
* Config resolution (SONOR_API_KEY → `x-api-key`, SONOR_API_URL, the legacy
|
|
52
|
+
* UPTRADE_API_KEY fallback) and the `https://api.sonor.io` base all live in core.
|
|
53
|
+
*
|
|
54
|
+
* NOTE: importing this module pulls core's `server-only` guard, so it (and any
|
|
55
|
+
* barrel that re-exports it, including the package root and AgencySiteKitLayout)
|
|
56
|
+
* is server-only. Client components must import from
|
|
57
|
+
* `@sonordev/agency-site-kit/portfolio/client`.
|
|
46
58
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
apiUrl: string;
|
|
50
|
-
}
|
|
59
|
+
|
|
60
|
+
type ApiConfig = ServerApiConfig;
|
|
51
61
|
/**
|
|
52
|
-
* Reads API configuration from environment
|
|
53
|
-
*
|
|
54
|
-
*
|
|
62
|
+
* Reads API configuration from overrides → environment (delegates to core's
|
|
63
|
+
* `resolveServerApiConfig`). Returns null and warns when no API key is found, so
|
|
64
|
+
* portfolio data degrades gracefully instead of throwing.
|
|
55
65
|
*/
|
|
56
66
|
declare function getApiConfig(): ApiConfig | null;
|
|
57
67
|
interface ApiFetchOptions extends Omit<RequestInit, 'headers'> {
|
|
@@ -71,12 +81,17 @@ interface ApiFetchOptions extends Omit<RequestInit, 'headers'> {
|
|
|
71
81
|
apiUrl?: string;
|
|
72
82
|
}
|
|
73
83
|
/**
|
|
74
|
-
* Fetch wrapper for the public portfolio API
|
|
84
|
+
* Fetch wrapper for the public portfolio API — delegates to core's
|
|
85
|
+
* `serverApiFetch`.
|
|
75
86
|
*
|
|
76
|
-
* - Injects `x-api-key`
|
|
87
|
+
* - Injects `x-api-key` automatically
|
|
77
88
|
* - Parses JSON responses
|
|
78
|
-
* - Uses Next.js ISR caching via `next.revalidate`
|
|
79
|
-
* - Returns null on failure instead of throwing
|
|
89
|
+
* - Uses Next.js ISR caching via `next.revalidate` (+ optional cache `tags`)
|
|
90
|
+
* - Returns null on failure (or a missing key) instead of throwing
|
|
91
|
+
*
|
|
92
|
+
* Single-attempt by design — portfolio pages degrade gracefully rather than
|
|
93
|
+
* hard-failing. (Core's fetcher supports retry/backoff; this preserves
|
|
94
|
+
* agency-site-kit's original behavior.)
|
|
80
95
|
*/
|
|
81
96
|
declare function apiFetch<T>(path: string, options?: ApiFetchOptions): Promise<T | null>;
|
|
82
97
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { B as BrandConfig } from './types-DL4t_Cfa.js';
|
|
2
2
|
export { M as MetricSource, e as MetricsDelta, f as MetricsSnapshot, g as PORTFOLIO_SECTION_TYPES, h as PortfolioAnnotation, i as PortfolioBeforeAfterData, j as PortfolioCTAData, k as PortfolioChallengeItem, l as PortfolioChallengesData, m as PortfolioConfigResponse, n as PortfolioConversionFunnelData, o as PortfolioDetailsData, p as PortfolioFeatureSpotlightData, q as PortfolioFunnelStage, r as PortfolioGalleryData, s as PortfolioGalleryImage, t as PortfolioHeroData, u as PortfolioItem, P as PortfolioItemFull, v as PortfolioKPI, a as PortfolioListResponse, w as PortfolioMetricsDataPoint, x as PortfolioMetricsTimelineData, y as PortfolioResultItem, z as PortfolioResultsData, A as PortfolioSection, d as PortfolioSectionData, c as PortfolioSectionDataMap, b as PortfolioSectionType, C as PortfolioSeoData, D as PortfolioServiceItem, E as PortfolioServicesData, F as PortfolioStrategyData, G as PortfolioStrategyPhase, H as PortfolioTeamData, I as PortfolioTeamMember, J as PortfolioTechItem, K as PortfolioTechStackData, L as PortfolioTestimonialData, N as PortfolioVideoData, S as SanityImageRef } from './types-DL4t_Cfa.js';
|
|
3
|
+
import { ServerApiConfig } from '@sonordev/site-kit/server';
|
|
3
4
|
import React from 'react';
|
|
4
5
|
export { AgencySiteKitLayout } from './layout/index.js';
|
|
5
6
|
export { PortfolioPage } from './portfolio/index.js';
|
|
@@ -41,17 +42,26 @@ declare function mergeBrandConfig(partial: Partial<BrandConfig>): BrandConfig;
|
|
|
41
42
|
/**
|
|
42
43
|
* @sonordev/agency-site-kit — API fetch utilities
|
|
43
44
|
*
|
|
44
|
-
* Server-side only.
|
|
45
|
-
*
|
|
45
|
+
* Server-side only. Thin adapters over core `@sonordev/site-kit`'s shared server
|
|
46
|
+
* fetcher (`@sonordev/site-kit/server`) — the single source of truth for the
|
|
47
|
+
* Sonor server data plane. This package no longer ships its own `apiFetch` copy;
|
|
48
|
+
* it reuses core's so the two can never drift (monorepo CLAUDE.md,
|
|
49
|
+
* "Consolidate Duplicated Logic").
|
|
50
|
+
*
|
|
51
|
+
* Config resolution (SONOR_API_KEY → `x-api-key`, SONOR_API_URL, the legacy
|
|
52
|
+
* UPTRADE_API_KEY fallback) and the `https://api.sonor.io` base all live in core.
|
|
53
|
+
*
|
|
54
|
+
* NOTE: importing this module pulls core's `server-only` guard, so it (and any
|
|
55
|
+
* barrel that re-exports it, including the package root and AgencySiteKitLayout)
|
|
56
|
+
* is server-only. Client components must import from
|
|
57
|
+
* `@sonordev/agency-site-kit/portfolio/client`.
|
|
46
58
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
apiUrl: string;
|
|
50
|
-
}
|
|
59
|
+
|
|
60
|
+
type ApiConfig = ServerApiConfig;
|
|
51
61
|
/**
|
|
52
|
-
* Reads API configuration from environment
|
|
53
|
-
*
|
|
54
|
-
*
|
|
62
|
+
* Reads API configuration from overrides → environment (delegates to core's
|
|
63
|
+
* `resolveServerApiConfig`). Returns null and warns when no API key is found, so
|
|
64
|
+
* portfolio data degrades gracefully instead of throwing.
|
|
55
65
|
*/
|
|
56
66
|
declare function getApiConfig(): ApiConfig | null;
|
|
57
67
|
interface ApiFetchOptions extends Omit<RequestInit, 'headers'> {
|
|
@@ -71,12 +81,17 @@ interface ApiFetchOptions extends Omit<RequestInit, 'headers'> {
|
|
|
71
81
|
apiUrl?: string;
|
|
72
82
|
}
|
|
73
83
|
/**
|
|
74
|
-
* Fetch wrapper for the public portfolio API
|
|
84
|
+
* Fetch wrapper for the public portfolio API — delegates to core's
|
|
85
|
+
* `serverApiFetch`.
|
|
75
86
|
*
|
|
76
|
-
* - Injects `x-api-key`
|
|
87
|
+
* - Injects `x-api-key` automatically
|
|
77
88
|
* - Parses JSON responses
|
|
78
|
-
* - Uses Next.js ISR caching via `next.revalidate`
|
|
79
|
-
* - Returns null on failure instead of throwing
|
|
89
|
+
* - Uses Next.js ISR caching via `next.revalidate` (+ optional cache `tags`)
|
|
90
|
+
* - Returns null on failure (or a missing key) instead of throwing
|
|
91
|
+
*
|
|
92
|
+
* Single-attempt by design — portfolio pages degrade gracefully rather than
|
|
93
|
+
* hard-failing. (Core's fetcher supports retry/backoff; this preserves
|
|
94
|
+
* agency-site-kit's original behavior.)
|
|
80
95
|
*/
|
|
81
96
|
declare function apiFetch<T>(path: string, options?: ApiFetchOptions): Promise<T | null>;
|
|
82
97
|
/**
|