@burdenoff/website-sdk 0.0.0-dev → 2026.510.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/analytics/index.js +0 -1
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +0 -1
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/client/index.d.mts +2 -5
- package/dist/client/index.d.ts +2 -5
- package/dist/client/index.js +2 -11
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +2 -11
- package/dist/client/index.mjs.map +1 -1
- package/dist/components/contact.d.mts +2 -77
- package/dist/components/contact.d.ts +2 -77
- package/dist/components/contact.js +9 -112
- package/dist/components/contact.js.map +1 -1
- package/dist/components/contact.mjs +10 -112
- package/dist/components/contact.mjs.map +1 -1
- package/dist/components/newsletter.js +6 -17
- package/dist/components/newsletter.js.map +1 -1
- package/dist/components/newsletter.mjs +6 -17
- package/dist/components/newsletter.mjs.map +1 -1
- package/dist/components/pricing.d.mts +0 -38
- package/dist/components/pricing.d.ts +0 -38
- package/dist/components/pricing.js +84 -267
- package/dist/components/pricing.js.map +1 -1
- package/dist/components/pricing.mjs +84 -267
- package/dist/components/pricing.mjs.map +1 -1
- package/dist/components/waitlist.js +2 -6
- package/dist/components/waitlist.js.map +1 -1
- package/dist/components/waitlist.mjs +2 -6
- package/dist/components/waitlist.mjs.map +1 -1
- package/dist/content/index.d.mts +9 -181
- package/dist/content/index.d.ts +9 -181
- package/dist/content/index.js +16 -2177
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +6 -2141
- package/dist/content/index.mjs.map +1 -1
- package/dist/index.d.mts +4 -42
- package/dist/index.d.ts +4 -42
- package/dist/index.js +373 -3919
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +378 -3905
- package/dist/index.mjs.map +1 -1
- package/dist/product/index.d.mts +0 -8
- package/dist/product/index.d.ts +0 -8
- package/dist/product/index.js +4 -24
- package/dist/product/index.js.map +1 -1
- package/dist/product/index.mjs +4 -24
- package/dist/product/index.mjs.map +1 -1
- package/package.json +3 -5
- package/dist/components/electron-download.d.mts +0 -32
- package/dist/components/electron-download.d.ts +0 -32
- package/dist/components/electron-download.js +0 -131
- package/dist/components/electron-download.js.map +0 -1
- package/dist/components/electron-download.mjs +0 -108
- package/dist/components/electron-download.mjs.map +0 -1
- package/dist/components/launch-countdown.d.mts +0 -15
- package/dist/components/launch-countdown.d.ts +0 -15
- package/dist/components/launch-countdown.js +0 -89
- package/dist/components/launch-countdown.js.map +0 -1
- package/dist/components/launch-countdown.mjs +0 -87
- package/dist/components/launch-countdown.mjs.map +0 -1
- package/dist/components/partners.d.mts +0 -88
- package/dist/components/partners.d.ts +0 -88
- package/dist/components/partners.js +0 -1169
- package/dist/components/partners.js.map +0 -1
- package/dist/components/partners.mjs +0 -1143
- package/dist/components/partners.mjs.map +0 -1
- package/dist/components/problems-solved.d.mts +0 -37
- package/dist/components/problems-solved.d.ts +0 -37
- package/dist/components/problems-solved.js +0 -157
- package/dist/components/problems-solved.js.map +0 -1
- package/dist/components/problems-solved.mjs +0 -135
- package/dist/components/problems-solved.mjs.map +0 -1
- package/dist/components/resource-links.d.mts +0 -28
- package/dist/components/resource-links.d.ts +0 -28
- package/dist/components/resource-links.js +0 -229
- package/dist/components/resource-links.js.map +0 -1
- package/dist/components/resource-links.mjs +0 -227
- package/dist/components/resource-links.mjs.map +0 -1
- package/dist/components/social-links.d.mts +0 -37
- package/dist/components/social-links.d.ts +0 -37
- package/dist/components/social-links.js +0 -268
- package/dist/components/social-links.js.map +0 -1
- package/dist/components/social-links.mjs +0 -266
- package/dist/components/social-links.mjs.map +0 -1
- package/dist/components/website-switcher.d.mts +0 -27
- package/dist/components/website-switcher.d.ts +0 -27
- package/dist/components/website-switcher.js +0 -168
- package/dist/components/website-switcher.js.map +0 -1
- package/dist/components/website-switcher.mjs +0 -146
- package/dist/components/website-switcher.mjs.map +0 -1
package/dist/analytics/index.js
CHANGED
|
@@ -16,7 +16,6 @@ function RybbitAnalytics({
|
|
|
16
16
|
const script = document.createElement("script");
|
|
17
17
|
script.src = scriptUrl;
|
|
18
18
|
script.defer = true;
|
|
19
|
-
script.crossOrigin = "anonymous";
|
|
20
19
|
script.setAttribute("data-site-id", siteId);
|
|
21
20
|
document.head.appendChild(script);
|
|
22
21
|
return () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/analytics/rybbit.tsx"],"names":["useEffect"],"mappings":";;;;AAoDO,SAAS,eAAA,CAAgB;AAAA,EAC9B,MAAA;AAAA,EACA,SAAA,GAAY,+CAAA;AAAA,EACZ,OAAA,GAAU;AACZ,CAAA,EAAyB;AACvB,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,MAAA,EAAQ;AAGzB,IAAA,MAAM,iBAAiB,QAAA,CAAS,aAAA;AAAA,MAC9B,wBAAwB,MAAM,CAAA,EAAA;AAAA,KAChC;AACA,IAAA,IAAI,cAAA,EAAgB;
|
|
1
|
+
{"version":3,"sources":["../../src/analytics/rybbit.tsx"],"names":["useEffect"],"mappings":";;;;AAoDO,SAAS,eAAA,CAAgB;AAAA,EAC9B,MAAA;AAAA,EACA,SAAA,GAAY,+CAAA;AAAA,EACZ,OAAA,GAAU;AACZ,CAAA,EAAyB;AACvB,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,MAAA,EAAQ;AAGzB,IAAA,MAAM,iBAAiB,QAAA,CAAS,aAAA;AAAA,MAC9B,wBAAwB,MAAM,CAAA,EAAA;AAAA,KAChC;AACA,IAAA,IAAI,cAAA,EAAgB;AAGpB,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAA,MAAA,CAAO,GAAA,GAAM,SAAA;AACb,IAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,IAAA,MAAA,CAAO,YAAA,CAAa,gBAAgB,MAAM,CAAA;AAC1C,IAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAGhC,IAAA,OAAO,MAAM;AACX,MAAA,MAAM,iBAAiB,QAAA,CAAS,aAAA;AAAA,QAC9B,wBAAwB,MAAM,CAAA,EAAA;AAAA,OAChC;AACA,MAAA,IAAI,cAAA,EAAgB;AAClB,QAAA,QAAA,CAAS,IAAA,CAAK,YAAY,cAAc,CAAA;AAAA,MAC1C;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,SAAA,EAAW,OAAO,CAAC,CAAA;AAG/B,EAAA,OAAO,IAAA;AACT","file":"index.js","sourcesContent":["/**\n * Rybbit Analytics Component\n *\n * Injects the Rybbit tracking script into the page head.\n * Works with both Vite (CSR) and Next.js (SSR/CSR) applications.\n *\n * @example\n * ```tsx\n * // In your app layout or root component\n * import { RybbitAnalytics } from '@burdenoff/website-sdk/analytics'\n *\n * function App() {\n * return (\n * <>\n * <RybbitAnalytics siteId=\"1\" />\n * <Router>...</Router>\n * </>\n * )\n * }\n * ```\n */\n\"use client\";\n\nimport { useEffect } from \"react\";\n\nexport interface RybbitAnalyticsProps {\n /**\n * Rybbit site ID (obtained from the Rybbit dashboard)\n */\n siteId: string;\n\n /**\n * Rybbit script URL (defaults to the self-hosted instance)\n * @default \"https://analytics.burdenoff.com/api/script.js\"\n */\n scriptUrl?: string;\n\n /**\n * Whether to enable tracking (useful for disabling in development)\n * @default true\n */\n enabled?: boolean;\n}\n\n/**\n * RybbitAnalytics - Embeds the Rybbit analytics tracking script.\n *\n * The script is loaded asynchronously with `defer` to avoid blocking page load.\n * It automatically tracks page views and user interactions.\n *\n * In development mode (when `enabled` is false), no script is loaded.\n */\nexport function RybbitAnalytics({\n siteId,\n scriptUrl = \"https://analytics.burdenoff.com/api/script.js\",\n enabled = true,\n}: RybbitAnalyticsProps) {\n useEffect(() => {\n if (!enabled || !siteId) return;\n\n // Check if script already exists\n const existingScript = document.querySelector(\n `script[data-site-id=\"${siteId}\"]`,\n );\n if (existingScript) return;\n\n // Create and inject the script\n const script = document.createElement(\"script\");\n script.src = scriptUrl;\n script.defer = true;\n script.setAttribute(\"data-site-id\", siteId);\n document.head.appendChild(script);\n\n // Cleanup on unmount\n return () => {\n const scriptToRemove = document.querySelector(\n `script[data-site-id=\"${siteId}\"]`,\n );\n if (scriptToRemove) {\n document.head.removeChild(scriptToRemove);\n }\n };\n }, [siteId, scriptUrl, enabled]);\n\n // This component renders nothing - it only manages the script tag\n return null;\n}\n"]}
|
package/dist/analytics/index.mjs
CHANGED
|
@@ -14,7 +14,6 @@ function RybbitAnalytics({
|
|
|
14
14
|
const script = document.createElement("script");
|
|
15
15
|
script.src = scriptUrl;
|
|
16
16
|
script.defer = true;
|
|
17
|
-
script.crossOrigin = "anonymous";
|
|
18
17
|
script.setAttribute("data-site-id", siteId);
|
|
19
18
|
document.head.appendChild(script);
|
|
20
19
|
return () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/analytics/rybbit.tsx"],"names":[],"mappings":";;AAoDO,SAAS,eAAA,CAAgB;AAAA,EAC9B,MAAA;AAAA,EACA,SAAA,GAAY,+CAAA;AAAA,EACZ,OAAA,GAAU;AACZ,CAAA,EAAyB;AACvB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,MAAA,EAAQ;AAGzB,IAAA,MAAM,iBAAiB,QAAA,CAAS,aAAA;AAAA,MAC9B,wBAAwB,MAAM,CAAA,EAAA;AAAA,KAChC;AACA,IAAA,IAAI,cAAA,EAAgB;
|
|
1
|
+
{"version":3,"sources":["../../src/analytics/rybbit.tsx"],"names":[],"mappings":";;AAoDO,SAAS,eAAA,CAAgB;AAAA,EAC9B,MAAA;AAAA,EACA,SAAA,GAAY,+CAAA;AAAA,EACZ,OAAA,GAAU;AACZ,CAAA,EAAyB;AACvB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,MAAA,EAAQ;AAGzB,IAAA,MAAM,iBAAiB,QAAA,CAAS,aAAA;AAAA,MAC9B,wBAAwB,MAAM,CAAA,EAAA;AAAA,KAChC;AACA,IAAA,IAAI,cAAA,EAAgB;AAGpB,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAA,MAAA,CAAO,GAAA,GAAM,SAAA;AACb,IAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,IAAA,MAAA,CAAO,YAAA,CAAa,gBAAgB,MAAM,CAAA;AAC1C,IAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAGhC,IAAA,OAAO,MAAM;AACX,MAAA,MAAM,iBAAiB,QAAA,CAAS,aAAA;AAAA,QAC9B,wBAAwB,MAAM,CAAA,EAAA;AAAA,OAChC;AACA,MAAA,IAAI,cAAA,EAAgB;AAClB,QAAA,QAAA,CAAS,IAAA,CAAK,YAAY,cAAc,CAAA;AAAA,MAC1C;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,SAAA,EAAW,OAAO,CAAC,CAAA;AAG/B,EAAA,OAAO,IAAA;AACT","file":"index.mjs","sourcesContent":["/**\n * Rybbit Analytics Component\n *\n * Injects the Rybbit tracking script into the page head.\n * Works with both Vite (CSR) and Next.js (SSR/CSR) applications.\n *\n * @example\n * ```tsx\n * // In your app layout or root component\n * import { RybbitAnalytics } from '@burdenoff/website-sdk/analytics'\n *\n * function App() {\n * return (\n * <>\n * <RybbitAnalytics siteId=\"1\" />\n * <Router>...</Router>\n * </>\n * )\n * }\n * ```\n */\n\"use client\";\n\nimport { useEffect } from \"react\";\n\nexport interface RybbitAnalyticsProps {\n /**\n * Rybbit site ID (obtained from the Rybbit dashboard)\n */\n siteId: string;\n\n /**\n * Rybbit script URL (defaults to the self-hosted instance)\n * @default \"https://analytics.burdenoff.com/api/script.js\"\n */\n scriptUrl?: string;\n\n /**\n * Whether to enable tracking (useful for disabling in development)\n * @default true\n */\n enabled?: boolean;\n}\n\n/**\n * RybbitAnalytics - Embeds the Rybbit analytics tracking script.\n *\n * The script is loaded asynchronously with `defer` to avoid blocking page load.\n * It automatically tracks page views and user interactions.\n *\n * In development mode (when `enabled` is false), no script is loaded.\n */\nexport function RybbitAnalytics({\n siteId,\n scriptUrl = \"https://analytics.burdenoff.com/api/script.js\",\n enabled = true,\n}: RybbitAnalyticsProps) {\n useEffect(() => {\n if (!enabled || !siteId) return;\n\n // Check if script already exists\n const existingScript = document.querySelector(\n `script[data-site-id=\"${siteId}\"]`,\n );\n if (existingScript) return;\n\n // Create and inject the script\n const script = document.createElement(\"script\");\n script.src = scriptUrl;\n script.defer = true;\n script.setAttribute(\"data-site-id\", siteId);\n document.head.appendChild(script);\n\n // Cleanup on unmount\n return () => {\n const scriptToRemove = document.querySelector(\n `script[data-site-id=\"${siteId}\"]`,\n );\n if (scriptToRemove) {\n document.head.removeChild(scriptToRemove);\n }\n };\n }, [siteId, scriptUrl, enabled]);\n\n // This component renders nothing - it only manages the script tag\n return null;\n}\n"]}
|
package/dist/client/index.d.mts
CHANGED
|
@@ -9,8 +9,7 @@ import 'react';
|
|
|
9
9
|
* but scoped to the website SDK's needs (global gateway only, no workspace gateway).
|
|
10
10
|
*
|
|
11
11
|
* Resolution priority:
|
|
12
|
-
* 1. Explicit `gatewayUrl` passed in config — used as-is
|
|
13
|
-
* absolute, non-localhost URL (relative/invalid values fall through)
|
|
12
|
+
* 1. Explicit `gatewayUrl` passed in config (non-empty) — used as-is
|
|
14
13
|
* 2. Hostname-based auto-detection:
|
|
15
14
|
* - Production domains (vibecontrols.com, bigconsole.com, ...) → prod gateway
|
|
16
15
|
* - Alpha domains (alpha.vibecontrols.com, ...) → alpha gateway
|
|
@@ -19,9 +18,7 @@ import 'react';
|
|
|
19
18
|
/**
|
|
20
19
|
* Resolve the global gateway URL at runtime based on the current hostname.
|
|
21
20
|
*
|
|
22
|
-
* @param explicitUrl - URL explicitly passed in SDK config
|
|
23
|
-
* it is a valid absolute, non-localhost URL; relative/invalid values are
|
|
24
|
-
* ignored so hostname auto-detection can resolve the correct gateway.
|
|
21
|
+
* @param explicitUrl - URL explicitly passed in SDK config (takes precedence if non-empty)
|
|
25
22
|
* @returns Resolved gateway URL
|
|
26
23
|
*/
|
|
27
24
|
declare function resolveGatewayUrl(explicitUrl?: string): string;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -9,8 +9,7 @@ import 'react';
|
|
|
9
9
|
* but scoped to the website SDK's needs (global gateway only, no workspace gateway).
|
|
10
10
|
*
|
|
11
11
|
* Resolution priority:
|
|
12
|
-
* 1. Explicit `gatewayUrl` passed in config — used as-is
|
|
13
|
-
* absolute, non-localhost URL (relative/invalid values fall through)
|
|
12
|
+
* 1. Explicit `gatewayUrl` passed in config (non-empty) — used as-is
|
|
14
13
|
* 2. Hostname-based auto-detection:
|
|
15
14
|
* - Production domains (vibecontrols.com, bigconsole.com, ...) → prod gateway
|
|
16
15
|
* - Alpha domains (alpha.vibecontrols.com, ...) → alpha gateway
|
|
@@ -19,9 +18,7 @@ import 'react';
|
|
|
19
18
|
/**
|
|
20
19
|
* Resolve the global gateway URL at runtime based on the current hostname.
|
|
21
20
|
*
|
|
22
|
-
* @param explicitUrl - URL explicitly passed in SDK config
|
|
23
|
-
* it is a valid absolute, non-localhost URL; relative/invalid values are
|
|
24
|
-
* ignored so hostname auto-detection can resolve the correct gateway.
|
|
21
|
+
* @param explicitUrl - URL explicitly passed in SDK config (takes precedence if non-empty)
|
|
25
22
|
* @returns Resolved gateway URL
|
|
26
23
|
*/
|
|
27
24
|
declare function resolveGatewayUrl(explicitUrl?: string): string;
|
package/dist/client/index.js
CHANGED
|
@@ -156,7 +156,6 @@ function getLocalFallback() {
|
|
|
156
156
|
}
|
|
157
157
|
var PRODUCT_DOMAINS = [
|
|
158
158
|
"burdenoff.com",
|
|
159
|
-
"algoshred.com",
|
|
160
159
|
"vibecontrols.com",
|
|
161
160
|
"fluidgrids.com",
|
|
162
161
|
"botlit.ai",
|
|
@@ -201,25 +200,17 @@ function isAlphaHost(hostname) {
|
|
|
201
200
|
);
|
|
202
201
|
}
|
|
203
202
|
function resolveGatewayUrl(explicitUrl) {
|
|
204
|
-
if (explicitUrl &&
|
|
203
|
+
if (explicitUrl && !isLocalhostUrl(explicitUrl)) {
|
|
205
204
|
return explicitUrl;
|
|
206
205
|
}
|
|
207
206
|
if (typeof window === "undefined") {
|
|
208
|
-
return explicitUrl
|
|
207
|
+
return explicitUrl || getLocalFallback();
|
|
209
208
|
}
|
|
210
209
|
const hostname = window.location.hostname.toLowerCase();
|
|
211
210
|
if (isProductionHost(hostname)) return PROD_GATEWAY;
|
|
212
211
|
if (isAlphaHost(hostname)) return ALPHA_GATEWAY;
|
|
213
212
|
return getLocalFallback();
|
|
214
213
|
}
|
|
215
|
-
function isAbsoluteUrl(url) {
|
|
216
|
-
try {
|
|
217
|
-
new URL(url);
|
|
218
|
-
return true;
|
|
219
|
-
} catch {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
214
|
function isLocalhostUrl(url) {
|
|
224
215
|
try {
|
|
225
216
|
const parsed = new URL(url);
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/security.ts","../../src/client/graphql-client.ts","../../src/client/gateway-resolver.ts","../../src/client/provider.tsx"],"names":["createContext","useMemo","useContext"],"mappings":"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","file":"index.js","sourcesContent":["const LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\"]);\nconst RESERVED_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"x-actor-id\",\n \"x-actor-type\",\n \"x-consent-granted-by\",\n \"x-consent-scope\",\n \"x-consent-granted-at\",\n \"x-consent-expires-at\",\n]);\n\nexport function validateGatewayUrl(value: string): string {\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n throw new Error(\"Gateway URL must be a valid absolute URL\");\n }\n\n if (url.protocol === \"https:\") {\n return url.toString();\n }\n\n if (\n url.protocol === \"http:\" &&\n LOOPBACK_HOSTS.has(url.hostname.toLowerCase())\n ) {\n return url.toString();\n }\n\n throw new Error(\"Gateway URL must use HTTPS unless it targets localhost\");\n}\n\nexport function sanitizeHeaders(\n headers?: Record<string, string>,\n): Record<string, string> | undefined {\n if (!headers) return undefined;\n\n return Object.fromEntries(\n Object.entries(headers).filter(\n ([key]) => !RESERVED_HEADERS.has(key.toLowerCase()),\n ),\n );\n}\n","/**\n * Lightweight GraphQL client for the Burdenoff Web SDK.\n * Uses native fetch - zero external dependencies.\n * All calls are unauthenticated (public gateway operations only).\n */\n\nimport { sanitizeHeaders, validateGatewayUrl } from \"./security\";\n\nexport interface WebSDKClientConfig {\n /** Global public gateway URL (e.g. \"https://api.burdenoff.com/global/graphql\") */\n gatewayUrl: string;\n /** Platform product ID for scoping operations */\n productId: string;\n /** Product slug (used for public product queries) */\n productSlug?: string;\n /** reCAPTCHA site key (if not fetched from product config) */\n recaptchaSiteKey?: string;\n /** Rybbit analytics site ID */\n rybbitSiteId?: string;\n /** Custom headers to include in all requests */\n headers?: Record<string, string>;\n}\n\nexport interface GraphQLResponse<T = Record<string, unknown>> {\n data?: T;\n errors?: Array<{\n message: string;\n locations?: Array<{ line: number; column: number }>;\n path?: Array<string | number>;\n extensions?: Record<string, unknown>;\n }>;\n}\n\nexport class WebSDKClient {\n private config: WebSDKClientConfig;\n private queryCache = new Map<\n string,\n { data: GraphQLResponse<unknown>; ts: number }\n >();\n private inflight = new Map<string, Promise<GraphQLResponse<unknown>>>();\n private static CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n private static MAX_CACHE_ENTRIES = 100;\n\n constructor(config: WebSDKClientConfig) {\n this.config = {\n ...config,\n gatewayUrl: validateGatewayUrl(config.gatewayUrl),\n headers: sanitizeHeaders(config.headers),\n };\n }\n\n getConfig(): WebSDKClientConfig {\n return this.config;\n }\n\n /**\n * Execute a GraphQL query (with deduplication and caching)\n */\n async query<T = Record<string, unknown>>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n const cacheKey = JSON.stringify({ query, variables });\n\n // Return cached response if still valid, evict if expired\n const cached = this.queryCache.get(cacheKey);\n if (cached) {\n if (Date.now() - cached.ts < WebSDKClient.CACHE_TTL) {\n return cached.data as GraphQLResponse<T>;\n }\n this.queryCache.delete(cacheKey);\n }\n\n // Deduplicate: if same query is already in-flight, reuse the promise\n const existing = this.inflight.get(cacheKey);\n if (existing) {\n return existing as Promise<GraphQLResponse<T>>;\n }\n\n const promise = this.request<T>(query, variables)\n .then((result) => {\n // Cache successful responses only\n if (result.data && !result.errors?.length) {\n if (this.queryCache.size >= WebSDKClient.MAX_CACHE_ENTRIES) {\n const oldestEntry = this.queryCache.keys().next().value;\n if (oldestEntry) {\n this.queryCache.delete(oldestEntry);\n }\n }\n this.queryCache.set(cacheKey, { data: result, ts: Date.now() });\n }\n return result;\n })\n .finally(() => {\n this.inflight.delete(cacheKey);\n });\n\n this.inflight.set(cacheKey, promise);\n return promise;\n }\n\n /**\n * Execute a GraphQL mutation\n */\n async mutate<T = Record<string, unknown>>(\n mutation: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n return this.request<T>(mutation, variables);\n }\n\n private async request<T>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n try {\n const response = await fetch(this.config.gatewayUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-product-id\": this.config.productId,\n ...this.config.headers,\n },\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n console.error(\n \"[WebSDK] GraphQL request failed\",\n response.status,\n response.statusText,\n );\n return {\n errors: [\n {\n message: `HTTP ${response.status}: ${response.statusText}`,\n extensions: { code: `HTTP_${response.status}` },\n },\n ],\n };\n }\n\n return (await response.json()) as GraphQLResponse<T>;\n } catch (error) {\n console.error(\n \"[WebSDK] Network error\",\n error instanceof Error ? error.message : \"unknown error\",\n );\n return {\n errors: [\n {\n message: error instanceof Error ? error.message : \"Network error\",\n extensions: { code: \"NETWORK_ERROR\" },\n },\n ],\n };\n }\n }\n}\n","/**\n * Runtime gateway URL resolution for product marketing websites.\n *\n * Mirrors the app shell pattern from @burdenoff/fe-libs/shared/config/gatewayUrls\n * but scoped to the website SDK's needs (global gateway only, no workspace gateway).\n *\n * Resolution priority:\n * 1. Explicit `gatewayUrl` passed in config — used as-is only if it is a valid\n * absolute, non-localhost URL (relative/invalid values fall through)\n * 2. Hostname-based auto-detection:\n * - Production domains (vibecontrols.com, bigconsole.com, ...) → prod gateway\n * - Alpha domains (alpha.vibecontrols.com, ...) → alpha gateway\n * - Everything else (localhost, *.local.burdenoff.com) → relative `/global/graphql`\n */\n\nconst PROD_GATEWAY = \"https://graphql.burdenoff.com/global/graphql\";\nconst ALPHA_GATEWAY = \"https://alphagraphql.burdenoff.com/global/graphql\";\n/**\n * Build the local fallback URL. In a browser context we need an absolute\n * URL (the SDK's security validator rejects relative paths), so we\n * prepend `window.location.origin`. During SSR/build (no window) we\n * return a placeholder localhost URL that the validator accepts.\n */\nfunction getLocalFallback(): string {\n if (typeof window !== \"undefined\") {\n return `${window.location.origin}/global/graphql`;\n }\n return \"http://localhost:4000/global/graphql\";\n}\n\n/**\n * All known product domains. When the website is served from any of these,\n * we can determine the environment from the subdomain pattern.\n */\nconst PRODUCT_DOMAINS = [\n \"burdenoff.com\",\n \"algoshred.com\",\n \"vibecontrols.com\",\n \"fluidgrids.com\",\n \"botlit.ai\",\n \"bigconsole.com\",\n \"gethealthybowl.com\",\n \"headshotmarketing.com\",\n \"timecampus.com\",\n \"ggnomad.com\",\n \"movethewheels.com\",\n \"crewfoundry.com\",\n \"planmagnet.com\",\n \"eventfullymanaged.com\",\n \"kadaikodi.com\",\n \"assethandler.com\",\n \"technospam.com\",\n \"semanticfed.com\",\n \"manufacturedops.com\",\n \"theglobalfuel.com\",\n \"housingvista.com\",\n \"proserviceworld.com\",\n \"mybodyshield.com\",\n \"artistrybase.com\",\n \"collabkin.com\",\n \"brainyrich.com\",\n \"theculturepost.com\",\n \"comradecircle.com\",\n \"cosmicintersection.com\",\n \"govcitizenhub.com\",\n \"intelwatchtower.com\",\n \"ecoimpacthub.com\",\n \"buildmyiq.com\",\n \"fluidgrids.ai\",\n];\n\nfunction isProductionHost(hostname: string): boolean {\n return PRODUCT_DOMAINS.some(\n (d) => hostname === d || hostname === `www.${d}` || hostname === `app.${d}`,\n );\n}\n\nfunction isAlphaHost(hostname: string): boolean {\n return PRODUCT_DOMAINS.some(\n (d) =>\n hostname === `alpha.${d}` ||\n hostname === `alphaapp.${d}` ||\n hostname === `staging.${d}`,\n );\n}\n\n/**\n * Resolve the global gateway URL at runtime based on the current hostname.\n *\n * @param explicitUrl - URL explicitly passed in SDK config. Honored only when\n * it is a valid absolute, non-localhost URL; relative/invalid values are\n * ignored so hostname auto-detection can resolve the correct gateway.\n * @returns Resolved gateway URL\n */\nexport function resolveGatewayUrl(explicitUrl?: string): string {\n // Only honor an explicit URL if it's an *absolute* URL and not a localhost\n // dev default. Consumers commonly pass a relative fallback like\n // `'/global/graphql'` when their build-time env var is unset; treating that\n // as authoritative would skip hostname auto-detection and then fail the\n // absolute-URL validator (blank page). A relative/invalid value is ignored\n // here so detection can resolve the correct prod/alpha gateway.\n if (\n explicitUrl &&\n isAbsoluteUrl(explicitUrl) &&\n !isLocalhostUrl(explicitUrl)\n ) {\n return explicitUrl;\n }\n\n if (typeof window === \"undefined\") {\n // SSR / build-time — can't detect hostname. Use the explicit URL only if\n // it's absolute; otherwise the localhost fallback (validator-safe).\n return explicitUrl && isAbsoluteUrl(explicitUrl)\n ? explicitUrl\n : getLocalFallback();\n }\n\n const hostname = window.location.hostname.toLowerCase();\n\n if (isProductionHost(hostname)) return PROD_GATEWAY;\n if (isAlphaHost(hostname)) return ALPHA_GATEWAY;\n\n // Local dev, *.local.burdenoff.com, or unknown — use relative URL\n // which the Vite proxy (dev) or nginx (containerized) will handle.\n return getLocalFallback();\n}\n\nfunction isAbsoluteUrl(url: string): boolean {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isLocalhostUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n const h = parsed.hostname;\n return h === \"localhost\" || h === \"127.0.0.1\" || h === \"::1\";\n } catch {\n return false;\n }\n}\n","/**\n * React context provider for the Web SDK client.\n * Wraps the application with WebSDKClient configuration.\n */\n\"use client\";\n\nimport { createContext, useContext, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { resolveGatewayUrl } from \"./gateway-resolver\";\nimport { WebSDKClient, type WebSDKClientConfig } from \"./graphql-client\";\n\nconst WebSDKContext = createContext<WebSDKClient | null>(null);\n\nexport interface WebSDKProviderProps {\n /** SDK client configuration */\n config: WebSDKClientConfig;\n /** Child components */\n children: ReactNode;\n}\n\n/**\n * Provider component that makes the WebSDKClient available to all child components.\n *\n * @example\n * ```tsx\n * <WebSDKProvider config={{\n * gatewayUrl: \"https://api.burdenoff.com/global/graphql\",\n * productId: \"your-product-id\",\n * productSlug: \"your-product\",\n * }}>\n * <App />\n * </WebSDKProvider>\n * ```\n */\nexport function WebSDKProvider({ config, children }: WebSDKProviderProps) {\n const client = useMemo(() => {\n const resolvedConfig = {\n ...config,\n gatewayUrl: resolveGatewayUrl(config.gatewayUrl),\n };\n return new WebSDKClient(resolvedConfig);\n }, [config]);\n\n return (\n <WebSDKContext.Provider value={client}>{children}</WebSDKContext.Provider>\n );\n}\n\n/**\n * Hook to access the WebSDKClient instance.\n * Must be used within a WebSDKProvider.\n */\nexport function useWebSDK(): WebSDKClient {\n const client = useContext(WebSDKContext);\n if (!client) {\n throw new Error(\"useWebSDK must be used within a WebSDKProvider\");\n }\n return client;\n}\n\n/**\n * Hook to access the SDK configuration.\n * Useful for components that need config values like productId, recaptchaSiteKey, etc.\n */\nexport function useWebSDKConfig(): WebSDKClientConfig {\n const client = useWebSDK();\n return client.getConfig();\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/security.ts","../../src/client/graphql-client.ts","../../src/client/gateway-resolver.ts","../../src/client/provider.tsx"],"names":["createContext","useMemo","useContext"],"mappings":"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","file":"index.js","sourcesContent":["const LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\"]);\nconst RESERVED_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"x-actor-id\",\n \"x-actor-type\",\n \"x-consent-granted-by\",\n \"x-consent-scope\",\n \"x-consent-granted-at\",\n \"x-consent-expires-at\",\n]);\n\nexport function validateGatewayUrl(value: string): string {\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n throw new Error(\"Gateway URL must be a valid absolute URL\");\n }\n\n if (url.protocol === \"https:\") {\n return url.toString();\n }\n\n if (\n url.protocol === \"http:\" &&\n LOOPBACK_HOSTS.has(url.hostname.toLowerCase())\n ) {\n return url.toString();\n }\n\n throw new Error(\"Gateway URL must use HTTPS unless it targets localhost\");\n}\n\nexport function sanitizeHeaders(\n headers?: Record<string, string>,\n): Record<string, string> | undefined {\n if (!headers) return undefined;\n\n return Object.fromEntries(\n Object.entries(headers).filter(\n ([key]) => !RESERVED_HEADERS.has(key.toLowerCase()),\n ),\n );\n}\n","/**\n * Lightweight GraphQL client for the Burdenoff Web SDK.\n * Uses native fetch - zero external dependencies.\n * All calls are unauthenticated (public gateway operations only).\n */\n\nimport { sanitizeHeaders, validateGatewayUrl } from \"./security\";\n\nexport interface WebSDKClientConfig {\n /** Global public gateway URL (e.g. \"https://api.burdenoff.com/global/graphql\") */\n gatewayUrl: string;\n /** Platform product ID for scoping operations */\n productId: string;\n /** Product slug (used for public product queries) */\n productSlug?: string;\n /** reCAPTCHA site key (if not fetched from product config) */\n recaptchaSiteKey?: string;\n /** Rybbit analytics site ID */\n rybbitSiteId?: string;\n /** Custom headers to include in all requests */\n headers?: Record<string, string>;\n}\n\nexport interface GraphQLResponse<T = Record<string, unknown>> {\n data?: T;\n errors?: Array<{\n message: string;\n locations?: Array<{ line: number; column: number }>;\n path?: Array<string | number>;\n extensions?: Record<string, unknown>;\n }>;\n}\n\nexport class WebSDKClient {\n private config: WebSDKClientConfig;\n private queryCache = new Map<\n string,\n { data: GraphQLResponse<unknown>; ts: number }\n >();\n private inflight = new Map<string, Promise<GraphQLResponse<unknown>>>();\n private static CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n private static MAX_CACHE_ENTRIES = 100;\n\n constructor(config: WebSDKClientConfig) {\n this.config = {\n ...config,\n gatewayUrl: validateGatewayUrl(config.gatewayUrl),\n headers: sanitizeHeaders(config.headers),\n };\n }\n\n getConfig(): WebSDKClientConfig {\n return this.config;\n }\n\n /**\n * Execute a GraphQL query (with deduplication and caching)\n */\n async query<T = Record<string, unknown>>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n const cacheKey = JSON.stringify({ query, variables });\n\n // Return cached response if still valid, evict if expired\n const cached = this.queryCache.get(cacheKey);\n if (cached) {\n if (Date.now() - cached.ts < WebSDKClient.CACHE_TTL) {\n return cached.data as GraphQLResponse<T>;\n }\n this.queryCache.delete(cacheKey);\n }\n\n // Deduplicate: if same query is already in-flight, reuse the promise\n const existing = this.inflight.get(cacheKey);\n if (existing) {\n return existing as Promise<GraphQLResponse<T>>;\n }\n\n const promise = this.request<T>(query, variables)\n .then((result) => {\n // Cache successful responses only\n if (result.data && !result.errors?.length) {\n if (this.queryCache.size >= WebSDKClient.MAX_CACHE_ENTRIES) {\n const oldestEntry = this.queryCache.keys().next().value;\n if (oldestEntry) {\n this.queryCache.delete(oldestEntry);\n }\n }\n this.queryCache.set(cacheKey, { data: result, ts: Date.now() });\n }\n return result;\n })\n .finally(() => {\n this.inflight.delete(cacheKey);\n });\n\n this.inflight.set(cacheKey, promise);\n return promise;\n }\n\n /**\n * Execute a GraphQL mutation\n */\n async mutate<T = Record<string, unknown>>(\n mutation: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n return this.request<T>(mutation, variables);\n }\n\n private async request<T>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n try {\n const response = await fetch(this.config.gatewayUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-product-id\": this.config.productId,\n ...this.config.headers,\n },\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n console.error(\n \"[WebSDK] GraphQL request failed\",\n response.status,\n response.statusText,\n );\n return {\n errors: [\n {\n message: `HTTP ${response.status}: ${response.statusText}`,\n extensions: { code: `HTTP_${response.status}` },\n },\n ],\n };\n }\n\n return (await response.json()) as GraphQLResponse<T>;\n } catch (error) {\n console.error(\n \"[WebSDK] Network error\",\n error instanceof Error ? error.message : \"unknown error\",\n );\n return {\n errors: [\n {\n message: error instanceof Error ? error.message : \"Network error\",\n extensions: { code: \"NETWORK_ERROR\" },\n },\n ],\n };\n }\n }\n}\n","/**\n * Runtime gateway URL resolution for product marketing websites.\n *\n * Mirrors the app shell pattern from @burdenoff/fe-libs/shared/config/gatewayUrls\n * but scoped to the website SDK's needs (global gateway only, no workspace gateway).\n *\n * Resolution priority:\n * 1. Explicit `gatewayUrl` passed in config (non-empty) — used as-is\n * 2. Hostname-based auto-detection:\n * - Production domains (vibecontrols.com, bigconsole.com, ...) → prod gateway\n * - Alpha domains (alpha.vibecontrols.com, ...) → alpha gateway\n * - Everything else (localhost, *.local.burdenoff.com) → relative `/global/graphql`\n */\n\nconst PROD_GATEWAY = \"https://graphql.burdenoff.com/global/graphql\";\nconst ALPHA_GATEWAY = \"https://alphagraphql.burdenoff.com/global/graphql\";\n/**\n * Build the local fallback URL. In a browser context we need an absolute\n * URL (the SDK's security validator rejects relative paths), so we\n * prepend `window.location.origin`. During SSR/build (no window) we\n * return a placeholder localhost URL that the validator accepts.\n */\nfunction getLocalFallback(): string {\n if (typeof window !== \"undefined\") {\n return `${window.location.origin}/global/graphql`;\n }\n return \"http://localhost:4000/global/graphql\";\n}\n\n/**\n * All known product domains. When the website is served from any of these,\n * we can determine the environment from the subdomain pattern.\n */\nconst PRODUCT_DOMAINS = [\n \"burdenoff.com\",\n \"vibecontrols.com\",\n \"fluidgrids.com\",\n \"botlit.ai\",\n \"bigconsole.com\",\n \"gethealthybowl.com\",\n \"headshotmarketing.com\",\n \"timecampus.com\",\n \"ggnomad.com\",\n \"movethewheels.com\",\n \"crewfoundry.com\",\n \"planmagnet.com\",\n \"eventfullymanaged.com\",\n \"kadaikodi.com\",\n \"assethandler.com\",\n \"technospam.com\",\n \"semanticfed.com\",\n \"manufacturedops.com\",\n \"theglobalfuel.com\",\n \"housingvista.com\",\n \"proserviceworld.com\",\n \"mybodyshield.com\",\n \"artistrybase.com\",\n \"collabkin.com\",\n \"brainyrich.com\",\n \"theculturepost.com\",\n \"comradecircle.com\",\n \"cosmicintersection.com\",\n \"govcitizenhub.com\",\n \"intelwatchtower.com\",\n \"ecoimpacthub.com\",\n \"buildmyiq.com\",\n \"fluidgrids.ai\",\n];\n\nfunction isProductionHost(hostname: string): boolean {\n return PRODUCT_DOMAINS.some(\n (d) => hostname === d || hostname === `www.${d}` || hostname === `app.${d}`,\n );\n}\n\nfunction isAlphaHost(hostname: string): boolean {\n return PRODUCT_DOMAINS.some(\n (d) =>\n hostname === `alpha.${d}` ||\n hostname === `alphaapp.${d}` ||\n hostname === `staging.${d}`,\n );\n}\n\n/**\n * Resolve the global gateway URL at runtime based on the current hostname.\n *\n * @param explicitUrl - URL explicitly passed in SDK config (takes precedence if non-empty)\n * @returns Resolved gateway URL\n */\nexport function resolveGatewayUrl(explicitUrl?: string): string {\n // If an explicit URL is provided and it's not a localhost dev default,\n // respect it — the consumer knows what they want.\n if (explicitUrl && !isLocalhostUrl(explicitUrl)) {\n return explicitUrl;\n }\n\n if (typeof window === \"undefined\") {\n // SSR / build-time — can't detect hostname, use the explicit URL or fallback\n return explicitUrl || getLocalFallback();\n }\n\n const hostname = window.location.hostname.toLowerCase();\n\n if (isProductionHost(hostname)) return PROD_GATEWAY;\n if (isAlphaHost(hostname)) return ALPHA_GATEWAY;\n\n // Local dev, *.local.burdenoff.com, or unknown — use relative URL\n // which the Vite proxy (dev) or nginx (containerized) will handle.\n return getLocalFallback();\n}\n\nfunction isLocalhostUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n const h = parsed.hostname;\n return h === \"localhost\" || h === \"127.0.0.1\" || h === \"::1\";\n } catch {\n return false;\n }\n}\n","/**\n * React context provider for the Web SDK client.\n * Wraps the application with WebSDKClient configuration.\n */\n\"use client\";\n\nimport { createContext, useContext, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { resolveGatewayUrl } from \"./gateway-resolver\";\nimport { WebSDKClient, type WebSDKClientConfig } from \"./graphql-client\";\n\nconst WebSDKContext = createContext<WebSDKClient | null>(null);\n\nexport interface WebSDKProviderProps {\n /** SDK client configuration */\n config: WebSDKClientConfig;\n /** Child components */\n children: ReactNode;\n}\n\n/**\n * Provider component that makes the WebSDKClient available to all child components.\n *\n * @example\n * ```tsx\n * <WebSDKProvider config={{\n * gatewayUrl: \"https://api.burdenoff.com/global/graphql\",\n * productId: \"your-product-id\",\n * productSlug: \"your-product\",\n * }}>\n * <App />\n * </WebSDKProvider>\n * ```\n */\nexport function WebSDKProvider({ config, children }: WebSDKProviderProps) {\n const client = useMemo(() => {\n const resolvedConfig = {\n ...config,\n gatewayUrl: resolveGatewayUrl(config.gatewayUrl),\n };\n return new WebSDKClient(resolvedConfig);\n }, [config]);\n\n return (\n <WebSDKContext.Provider value={client}>{children}</WebSDKContext.Provider>\n );\n}\n\n/**\n * Hook to access the WebSDKClient instance.\n * Must be used within a WebSDKProvider.\n */\nexport function useWebSDK(): WebSDKClient {\n const client = useContext(WebSDKContext);\n if (!client) {\n throw new Error(\"useWebSDK must be used within a WebSDKProvider\");\n }\n return client;\n}\n\n/**\n * Hook to access the SDK configuration.\n * Useful for components that need config values like productId, recaptchaSiteKey, etc.\n */\nexport function useWebSDKConfig(): WebSDKClientConfig {\n const client = useWebSDK();\n return client.getConfig();\n}\n"]}
|
package/dist/client/index.mjs
CHANGED
|
@@ -154,7 +154,6 @@ function getLocalFallback() {
|
|
|
154
154
|
}
|
|
155
155
|
var PRODUCT_DOMAINS = [
|
|
156
156
|
"burdenoff.com",
|
|
157
|
-
"algoshred.com",
|
|
158
157
|
"vibecontrols.com",
|
|
159
158
|
"fluidgrids.com",
|
|
160
159
|
"botlit.ai",
|
|
@@ -199,25 +198,17 @@ function isAlphaHost(hostname) {
|
|
|
199
198
|
);
|
|
200
199
|
}
|
|
201
200
|
function resolveGatewayUrl(explicitUrl) {
|
|
202
|
-
if (explicitUrl &&
|
|
201
|
+
if (explicitUrl && !isLocalhostUrl(explicitUrl)) {
|
|
203
202
|
return explicitUrl;
|
|
204
203
|
}
|
|
205
204
|
if (typeof window === "undefined") {
|
|
206
|
-
return explicitUrl
|
|
205
|
+
return explicitUrl || getLocalFallback();
|
|
207
206
|
}
|
|
208
207
|
const hostname = window.location.hostname.toLowerCase();
|
|
209
208
|
if (isProductionHost(hostname)) return PROD_GATEWAY;
|
|
210
209
|
if (isAlphaHost(hostname)) return ALPHA_GATEWAY;
|
|
211
210
|
return getLocalFallback();
|
|
212
211
|
}
|
|
213
|
-
function isAbsoluteUrl(url) {
|
|
214
|
-
try {
|
|
215
|
-
new URL(url);
|
|
216
|
-
return true;
|
|
217
|
-
} catch {
|
|
218
|
-
return false;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
212
|
function isLocalhostUrl(url) {
|
|
222
213
|
try {
|
|
223
214
|
const parsed = new URL(url);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/security.ts","../../src/client/graphql-client.ts","../../src/client/gateway-resolver.ts","../../src/client/provider.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["const LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\"]);\nconst RESERVED_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"x-actor-id\",\n \"x-actor-type\",\n \"x-consent-granted-by\",\n \"x-consent-scope\",\n \"x-consent-granted-at\",\n \"x-consent-expires-at\",\n]);\n\nexport function validateGatewayUrl(value: string): string {\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n throw new Error(\"Gateway URL must be a valid absolute URL\");\n }\n\n if (url.protocol === \"https:\") {\n return url.toString();\n }\n\n if (\n url.protocol === \"http:\" &&\n LOOPBACK_HOSTS.has(url.hostname.toLowerCase())\n ) {\n return url.toString();\n }\n\n throw new Error(\"Gateway URL must use HTTPS unless it targets localhost\");\n}\n\nexport function sanitizeHeaders(\n headers?: Record<string, string>,\n): Record<string, string> | undefined {\n if (!headers) return undefined;\n\n return Object.fromEntries(\n Object.entries(headers).filter(\n ([key]) => !RESERVED_HEADERS.has(key.toLowerCase()),\n ),\n );\n}\n","/**\n * Lightweight GraphQL client for the Burdenoff Web SDK.\n * Uses native fetch - zero external dependencies.\n * All calls are unauthenticated (public gateway operations only).\n */\n\nimport { sanitizeHeaders, validateGatewayUrl } from \"./security\";\n\nexport interface WebSDKClientConfig {\n /** Global public gateway URL (e.g. \"https://api.burdenoff.com/global/graphql\") */\n gatewayUrl: string;\n /** Platform product ID for scoping operations */\n productId: string;\n /** Product slug (used for public product queries) */\n productSlug?: string;\n /** reCAPTCHA site key (if not fetched from product config) */\n recaptchaSiteKey?: string;\n /** Rybbit analytics site ID */\n rybbitSiteId?: string;\n /** Custom headers to include in all requests */\n headers?: Record<string, string>;\n}\n\nexport interface GraphQLResponse<T = Record<string, unknown>> {\n data?: T;\n errors?: Array<{\n message: string;\n locations?: Array<{ line: number; column: number }>;\n path?: Array<string | number>;\n extensions?: Record<string, unknown>;\n }>;\n}\n\nexport class WebSDKClient {\n private config: WebSDKClientConfig;\n private queryCache = new Map<\n string,\n { data: GraphQLResponse<unknown>; ts: number }\n >();\n private inflight = new Map<string, Promise<GraphQLResponse<unknown>>>();\n private static CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n private static MAX_CACHE_ENTRIES = 100;\n\n constructor(config: WebSDKClientConfig) {\n this.config = {\n ...config,\n gatewayUrl: validateGatewayUrl(config.gatewayUrl),\n headers: sanitizeHeaders(config.headers),\n };\n }\n\n getConfig(): WebSDKClientConfig {\n return this.config;\n }\n\n /**\n * Execute a GraphQL query (with deduplication and caching)\n */\n async query<T = Record<string, unknown>>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n const cacheKey = JSON.stringify({ query, variables });\n\n // Return cached response if still valid, evict if expired\n const cached = this.queryCache.get(cacheKey);\n if (cached) {\n if (Date.now() - cached.ts < WebSDKClient.CACHE_TTL) {\n return cached.data as GraphQLResponse<T>;\n }\n this.queryCache.delete(cacheKey);\n }\n\n // Deduplicate: if same query is already in-flight, reuse the promise\n const existing = this.inflight.get(cacheKey);\n if (existing) {\n return existing as Promise<GraphQLResponse<T>>;\n }\n\n const promise = this.request<T>(query, variables)\n .then((result) => {\n // Cache successful responses only\n if (result.data && !result.errors?.length) {\n if (this.queryCache.size >= WebSDKClient.MAX_CACHE_ENTRIES) {\n const oldestEntry = this.queryCache.keys().next().value;\n if (oldestEntry) {\n this.queryCache.delete(oldestEntry);\n }\n }\n this.queryCache.set(cacheKey, { data: result, ts: Date.now() });\n }\n return result;\n })\n .finally(() => {\n this.inflight.delete(cacheKey);\n });\n\n this.inflight.set(cacheKey, promise);\n return promise;\n }\n\n /**\n * Execute a GraphQL mutation\n */\n async mutate<T = Record<string, unknown>>(\n mutation: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n return this.request<T>(mutation, variables);\n }\n\n private async request<T>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n try {\n const response = await fetch(this.config.gatewayUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-product-id\": this.config.productId,\n ...this.config.headers,\n },\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n console.error(\n \"[WebSDK] GraphQL request failed\",\n response.status,\n response.statusText,\n );\n return {\n errors: [\n {\n message: `HTTP ${response.status}: ${response.statusText}`,\n extensions: { code: `HTTP_${response.status}` },\n },\n ],\n };\n }\n\n return (await response.json()) as GraphQLResponse<T>;\n } catch (error) {\n console.error(\n \"[WebSDK] Network error\",\n error instanceof Error ? error.message : \"unknown error\",\n );\n return {\n errors: [\n {\n message: error instanceof Error ? error.message : \"Network error\",\n extensions: { code: \"NETWORK_ERROR\" },\n },\n ],\n };\n }\n }\n}\n","/**\n * Runtime gateway URL resolution for product marketing websites.\n *\n * Mirrors the app shell pattern from @burdenoff/fe-libs/shared/config/gatewayUrls\n * but scoped to the website SDK's needs (global gateway only, no workspace gateway).\n *\n * Resolution priority:\n * 1. Explicit `gatewayUrl` passed in config — used as-is only if it is a valid\n * absolute, non-localhost URL (relative/invalid values fall through)\n * 2. Hostname-based auto-detection:\n * - Production domains (vibecontrols.com, bigconsole.com, ...) → prod gateway\n * - Alpha domains (alpha.vibecontrols.com, ...) → alpha gateway\n * - Everything else (localhost, *.local.burdenoff.com) → relative `/global/graphql`\n */\n\nconst PROD_GATEWAY = \"https://graphql.burdenoff.com/global/graphql\";\nconst ALPHA_GATEWAY = \"https://alphagraphql.burdenoff.com/global/graphql\";\n/**\n * Build the local fallback URL. In a browser context we need an absolute\n * URL (the SDK's security validator rejects relative paths), so we\n * prepend `window.location.origin`. During SSR/build (no window) we\n * return a placeholder localhost URL that the validator accepts.\n */\nfunction getLocalFallback(): string {\n if (typeof window !== \"undefined\") {\n return `${window.location.origin}/global/graphql`;\n }\n return \"http://localhost:4000/global/graphql\";\n}\n\n/**\n * All known product domains. When the website is served from any of these,\n * we can determine the environment from the subdomain pattern.\n */\nconst PRODUCT_DOMAINS = [\n \"burdenoff.com\",\n \"algoshred.com\",\n \"vibecontrols.com\",\n \"fluidgrids.com\",\n \"botlit.ai\",\n \"bigconsole.com\",\n \"gethealthybowl.com\",\n \"headshotmarketing.com\",\n \"timecampus.com\",\n \"ggnomad.com\",\n \"movethewheels.com\",\n \"crewfoundry.com\",\n \"planmagnet.com\",\n \"eventfullymanaged.com\",\n \"kadaikodi.com\",\n \"assethandler.com\",\n \"technospam.com\",\n \"semanticfed.com\",\n \"manufacturedops.com\",\n \"theglobalfuel.com\",\n \"housingvista.com\",\n \"proserviceworld.com\",\n \"mybodyshield.com\",\n \"artistrybase.com\",\n \"collabkin.com\",\n \"brainyrich.com\",\n \"theculturepost.com\",\n \"comradecircle.com\",\n \"cosmicintersection.com\",\n \"govcitizenhub.com\",\n \"intelwatchtower.com\",\n \"ecoimpacthub.com\",\n \"buildmyiq.com\",\n \"fluidgrids.ai\",\n];\n\nfunction isProductionHost(hostname: string): boolean {\n return PRODUCT_DOMAINS.some(\n (d) => hostname === d || hostname === `www.${d}` || hostname === `app.${d}`,\n );\n}\n\nfunction isAlphaHost(hostname: string): boolean {\n return PRODUCT_DOMAINS.some(\n (d) =>\n hostname === `alpha.${d}` ||\n hostname === `alphaapp.${d}` ||\n hostname === `staging.${d}`,\n );\n}\n\n/**\n * Resolve the global gateway URL at runtime based on the current hostname.\n *\n * @param explicitUrl - URL explicitly passed in SDK config. Honored only when\n * it is a valid absolute, non-localhost URL; relative/invalid values are\n * ignored so hostname auto-detection can resolve the correct gateway.\n * @returns Resolved gateway URL\n */\nexport function resolveGatewayUrl(explicitUrl?: string): string {\n // Only honor an explicit URL if it's an *absolute* URL and not a localhost\n // dev default. Consumers commonly pass a relative fallback like\n // `'/global/graphql'` when their build-time env var is unset; treating that\n // as authoritative would skip hostname auto-detection and then fail the\n // absolute-URL validator (blank page). A relative/invalid value is ignored\n // here so detection can resolve the correct prod/alpha gateway.\n if (\n explicitUrl &&\n isAbsoluteUrl(explicitUrl) &&\n !isLocalhostUrl(explicitUrl)\n ) {\n return explicitUrl;\n }\n\n if (typeof window === \"undefined\") {\n // SSR / build-time — can't detect hostname. Use the explicit URL only if\n // it's absolute; otherwise the localhost fallback (validator-safe).\n return explicitUrl && isAbsoluteUrl(explicitUrl)\n ? explicitUrl\n : getLocalFallback();\n }\n\n const hostname = window.location.hostname.toLowerCase();\n\n if (isProductionHost(hostname)) return PROD_GATEWAY;\n if (isAlphaHost(hostname)) return ALPHA_GATEWAY;\n\n // Local dev, *.local.burdenoff.com, or unknown — use relative URL\n // which the Vite proxy (dev) or nginx (containerized) will handle.\n return getLocalFallback();\n}\n\nfunction isAbsoluteUrl(url: string): boolean {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isLocalhostUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n const h = parsed.hostname;\n return h === \"localhost\" || h === \"127.0.0.1\" || h === \"::1\";\n } catch {\n return false;\n }\n}\n","/**\n * React context provider for the Web SDK client.\n * Wraps the application with WebSDKClient configuration.\n */\n\"use client\";\n\nimport { createContext, useContext, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { resolveGatewayUrl } from \"./gateway-resolver\";\nimport { WebSDKClient, type WebSDKClientConfig } from \"./graphql-client\";\n\nconst WebSDKContext = createContext<WebSDKClient | null>(null);\n\nexport interface WebSDKProviderProps {\n /** SDK client configuration */\n config: WebSDKClientConfig;\n /** Child components */\n children: ReactNode;\n}\n\n/**\n * Provider component that makes the WebSDKClient available to all child components.\n *\n * @example\n * ```tsx\n * <WebSDKProvider config={{\n * gatewayUrl: \"https://api.burdenoff.com/global/graphql\",\n * productId: \"your-product-id\",\n * productSlug: \"your-product\",\n * }}>\n * <App />\n * </WebSDKProvider>\n * ```\n */\nexport function WebSDKProvider({ config, children }: WebSDKProviderProps) {\n const client = useMemo(() => {\n const resolvedConfig = {\n ...config,\n gatewayUrl: resolveGatewayUrl(config.gatewayUrl),\n };\n return new WebSDKClient(resolvedConfig);\n }, [config]);\n\n return (\n <WebSDKContext.Provider value={client}>{children}</WebSDKContext.Provider>\n );\n}\n\n/**\n * Hook to access the WebSDKClient instance.\n * Must be used within a WebSDKProvider.\n */\nexport function useWebSDK(): WebSDKClient {\n const client = useContext(WebSDKContext);\n if (!client) {\n throw new Error(\"useWebSDK must be used within a WebSDKProvider\");\n }\n return client;\n}\n\n/**\n * Hook to access the SDK configuration.\n * Useful for components that need config values like productId, recaptchaSiteKey, etc.\n */\nexport function useWebSDKConfig(): WebSDKClientConfig {\n const client = useWebSDK();\n return client.getConfig();\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/security.ts","../../src/client/graphql-client.ts","../../src/client/gateway-resolver.ts","../../src/client/provider.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["const LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\"]);\nconst RESERVED_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"x-actor-id\",\n \"x-actor-type\",\n \"x-consent-granted-by\",\n \"x-consent-scope\",\n \"x-consent-granted-at\",\n \"x-consent-expires-at\",\n]);\n\nexport function validateGatewayUrl(value: string): string {\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n throw new Error(\"Gateway URL must be a valid absolute URL\");\n }\n\n if (url.protocol === \"https:\") {\n return url.toString();\n }\n\n if (\n url.protocol === \"http:\" &&\n LOOPBACK_HOSTS.has(url.hostname.toLowerCase())\n ) {\n return url.toString();\n }\n\n throw new Error(\"Gateway URL must use HTTPS unless it targets localhost\");\n}\n\nexport function sanitizeHeaders(\n headers?: Record<string, string>,\n): Record<string, string> | undefined {\n if (!headers) return undefined;\n\n return Object.fromEntries(\n Object.entries(headers).filter(\n ([key]) => !RESERVED_HEADERS.has(key.toLowerCase()),\n ),\n );\n}\n","/**\n * Lightweight GraphQL client for the Burdenoff Web SDK.\n * Uses native fetch - zero external dependencies.\n * All calls are unauthenticated (public gateway operations only).\n */\n\nimport { sanitizeHeaders, validateGatewayUrl } from \"./security\";\n\nexport interface WebSDKClientConfig {\n /** Global public gateway URL (e.g. \"https://api.burdenoff.com/global/graphql\") */\n gatewayUrl: string;\n /** Platform product ID for scoping operations */\n productId: string;\n /** Product slug (used for public product queries) */\n productSlug?: string;\n /** reCAPTCHA site key (if not fetched from product config) */\n recaptchaSiteKey?: string;\n /** Rybbit analytics site ID */\n rybbitSiteId?: string;\n /** Custom headers to include in all requests */\n headers?: Record<string, string>;\n}\n\nexport interface GraphQLResponse<T = Record<string, unknown>> {\n data?: T;\n errors?: Array<{\n message: string;\n locations?: Array<{ line: number; column: number }>;\n path?: Array<string | number>;\n extensions?: Record<string, unknown>;\n }>;\n}\n\nexport class WebSDKClient {\n private config: WebSDKClientConfig;\n private queryCache = new Map<\n string,\n { data: GraphQLResponse<unknown>; ts: number }\n >();\n private inflight = new Map<string, Promise<GraphQLResponse<unknown>>>();\n private static CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n private static MAX_CACHE_ENTRIES = 100;\n\n constructor(config: WebSDKClientConfig) {\n this.config = {\n ...config,\n gatewayUrl: validateGatewayUrl(config.gatewayUrl),\n headers: sanitizeHeaders(config.headers),\n };\n }\n\n getConfig(): WebSDKClientConfig {\n return this.config;\n }\n\n /**\n * Execute a GraphQL query (with deduplication and caching)\n */\n async query<T = Record<string, unknown>>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n const cacheKey = JSON.stringify({ query, variables });\n\n // Return cached response if still valid, evict if expired\n const cached = this.queryCache.get(cacheKey);\n if (cached) {\n if (Date.now() - cached.ts < WebSDKClient.CACHE_TTL) {\n return cached.data as GraphQLResponse<T>;\n }\n this.queryCache.delete(cacheKey);\n }\n\n // Deduplicate: if same query is already in-flight, reuse the promise\n const existing = this.inflight.get(cacheKey);\n if (existing) {\n return existing as Promise<GraphQLResponse<T>>;\n }\n\n const promise = this.request<T>(query, variables)\n .then((result) => {\n // Cache successful responses only\n if (result.data && !result.errors?.length) {\n if (this.queryCache.size >= WebSDKClient.MAX_CACHE_ENTRIES) {\n const oldestEntry = this.queryCache.keys().next().value;\n if (oldestEntry) {\n this.queryCache.delete(oldestEntry);\n }\n }\n this.queryCache.set(cacheKey, { data: result, ts: Date.now() });\n }\n return result;\n })\n .finally(() => {\n this.inflight.delete(cacheKey);\n });\n\n this.inflight.set(cacheKey, promise);\n return promise;\n }\n\n /**\n * Execute a GraphQL mutation\n */\n async mutate<T = Record<string, unknown>>(\n mutation: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n return this.request<T>(mutation, variables);\n }\n\n private async request<T>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n try {\n const response = await fetch(this.config.gatewayUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-product-id\": this.config.productId,\n ...this.config.headers,\n },\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n console.error(\n \"[WebSDK] GraphQL request failed\",\n response.status,\n response.statusText,\n );\n return {\n errors: [\n {\n message: `HTTP ${response.status}: ${response.statusText}`,\n extensions: { code: `HTTP_${response.status}` },\n },\n ],\n };\n }\n\n return (await response.json()) as GraphQLResponse<T>;\n } catch (error) {\n console.error(\n \"[WebSDK] Network error\",\n error instanceof Error ? error.message : \"unknown error\",\n );\n return {\n errors: [\n {\n message: error instanceof Error ? error.message : \"Network error\",\n extensions: { code: \"NETWORK_ERROR\" },\n },\n ],\n };\n }\n }\n}\n","/**\n * Runtime gateway URL resolution for product marketing websites.\n *\n * Mirrors the app shell pattern from @burdenoff/fe-libs/shared/config/gatewayUrls\n * but scoped to the website SDK's needs (global gateway only, no workspace gateway).\n *\n * Resolution priority:\n * 1. Explicit `gatewayUrl` passed in config (non-empty) — used as-is\n * 2. Hostname-based auto-detection:\n * - Production domains (vibecontrols.com, bigconsole.com, ...) → prod gateway\n * - Alpha domains (alpha.vibecontrols.com, ...) → alpha gateway\n * - Everything else (localhost, *.local.burdenoff.com) → relative `/global/graphql`\n */\n\nconst PROD_GATEWAY = \"https://graphql.burdenoff.com/global/graphql\";\nconst ALPHA_GATEWAY = \"https://alphagraphql.burdenoff.com/global/graphql\";\n/**\n * Build the local fallback URL. In a browser context we need an absolute\n * URL (the SDK's security validator rejects relative paths), so we\n * prepend `window.location.origin`. During SSR/build (no window) we\n * return a placeholder localhost URL that the validator accepts.\n */\nfunction getLocalFallback(): string {\n if (typeof window !== \"undefined\") {\n return `${window.location.origin}/global/graphql`;\n }\n return \"http://localhost:4000/global/graphql\";\n}\n\n/**\n * All known product domains. When the website is served from any of these,\n * we can determine the environment from the subdomain pattern.\n */\nconst PRODUCT_DOMAINS = [\n \"burdenoff.com\",\n \"vibecontrols.com\",\n \"fluidgrids.com\",\n \"botlit.ai\",\n \"bigconsole.com\",\n \"gethealthybowl.com\",\n \"headshotmarketing.com\",\n \"timecampus.com\",\n \"ggnomad.com\",\n \"movethewheels.com\",\n \"crewfoundry.com\",\n \"planmagnet.com\",\n \"eventfullymanaged.com\",\n \"kadaikodi.com\",\n \"assethandler.com\",\n \"technospam.com\",\n \"semanticfed.com\",\n \"manufacturedops.com\",\n \"theglobalfuel.com\",\n \"housingvista.com\",\n \"proserviceworld.com\",\n \"mybodyshield.com\",\n \"artistrybase.com\",\n \"collabkin.com\",\n \"brainyrich.com\",\n \"theculturepost.com\",\n \"comradecircle.com\",\n \"cosmicintersection.com\",\n \"govcitizenhub.com\",\n \"intelwatchtower.com\",\n \"ecoimpacthub.com\",\n \"buildmyiq.com\",\n \"fluidgrids.ai\",\n];\n\nfunction isProductionHost(hostname: string): boolean {\n return PRODUCT_DOMAINS.some(\n (d) => hostname === d || hostname === `www.${d}` || hostname === `app.${d}`,\n );\n}\n\nfunction isAlphaHost(hostname: string): boolean {\n return PRODUCT_DOMAINS.some(\n (d) =>\n hostname === `alpha.${d}` ||\n hostname === `alphaapp.${d}` ||\n hostname === `staging.${d}`,\n );\n}\n\n/**\n * Resolve the global gateway URL at runtime based on the current hostname.\n *\n * @param explicitUrl - URL explicitly passed in SDK config (takes precedence if non-empty)\n * @returns Resolved gateway URL\n */\nexport function resolveGatewayUrl(explicitUrl?: string): string {\n // If an explicit URL is provided and it's not a localhost dev default,\n // respect it — the consumer knows what they want.\n if (explicitUrl && !isLocalhostUrl(explicitUrl)) {\n return explicitUrl;\n }\n\n if (typeof window === \"undefined\") {\n // SSR / build-time — can't detect hostname, use the explicit URL or fallback\n return explicitUrl || getLocalFallback();\n }\n\n const hostname = window.location.hostname.toLowerCase();\n\n if (isProductionHost(hostname)) return PROD_GATEWAY;\n if (isAlphaHost(hostname)) return ALPHA_GATEWAY;\n\n // Local dev, *.local.burdenoff.com, or unknown — use relative URL\n // which the Vite proxy (dev) or nginx (containerized) will handle.\n return getLocalFallback();\n}\n\nfunction isLocalhostUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n const h = parsed.hostname;\n return h === \"localhost\" || h === \"127.0.0.1\" || h === \"::1\";\n } catch {\n return false;\n }\n}\n","/**\n * React context provider for the Web SDK client.\n * Wraps the application with WebSDKClient configuration.\n */\n\"use client\";\n\nimport { createContext, useContext, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { resolveGatewayUrl } from \"./gateway-resolver\";\nimport { WebSDKClient, type WebSDKClientConfig } from \"./graphql-client\";\n\nconst WebSDKContext = createContext<WebSDKClient | null>(null);\n\nexport interface WebSDKProviderProps {\n /** SDK client configuration */\n config: WebSDKClientConfig;\n /** Child components */\n children: ReactNode;\n}\n\n/**\n * Provider component that makes the WebSDKClient available to all child components.\n *\n * @example\n * ```tsx\n * <WebSDKProvider config={{\n * gatewayUrl: \"https://api.burdenoff.com/global/graphql\",\n * productId: \"your-product-id\",\n * productSlug: \"your-product\",\n * }}>\n * <App />\n * </WebSDKProvider>\n * ```\n */\nexport function WebSDKProvider({ config, children }: WebSDKProviderProps) {\n const client = useMemo(() => {\n const resolvedConfig = {\n ...config,\n gatewayUrl: resolveGatewayUrl(config.gatewayUrl),\n };\n return new WebSDKClient(resolvedConfig);\n }, [config]);\n\n return (\n <WebSDKContext.Provider value={client}>{children}</WebSDKContext.Provider>\n );\n}\n\n/**\n * Hook to access the WebSDKClient instance.\n * Must be used within a WebSDKProvider.\n */\nexport function useWebSDK(): WebSDKClient {\n const client = useContext(WebSDKContext);\n if (!client) {\n throw new Error(\"useWebSDK must be used within a WebSDKProvider\");\n }\n return client;\n}\n\n/**\n * Hook to access the SDK configuration.\n * Useful for components that need config values like productId, recaptchaSiteKey, etc.\n */\nexport function useWebSDKConfig(): WebSDKClientConfig {\n const client = useWebSDK();\n return client.getConfig();\n}\n"]}
|
|
@@ -36,39 +36,6 @@ interface ContactOption {
|
|
|
36
36
|
buttonText: string;
|
|
37
37
|
buttonLink: string;
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
* One entry in the contact-form Category dropdown. Each product website
|
|
41
|
-
* supplies its own allow-list via the `categories` prop so the
|
|
42
|
-
* vocabulary stays product-appropriate (e.g. VibeControls uses
|
|
43
|
-
* `plugin-developer`, a manufacturing site uses `dealer`). The backend
|
|
44
|
-
* accepts whatever `value` string is sent and persists it directly.
|
|
45
|
-
*
|
|
46
|
-
* `value` flows to the backend; `label` is what the user sees.
|
|
47
|
-
*/
|
|
48
|
-
interface ContactCategory {
|
|
49
|
-
/** Stored on the server. Short, kebab-case is conventional. */
|
|
50
|
-
value: string;
|
|
51
|
-
/** Human-readable label shown in the dropdown. */
|
|
52
|
-
label: string;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Conservative starter category list — Burdenoff-generic vocabulary
|
|
56
|
-
* (General / Sales / Support / Partnership / Press / Careers / Security).
|
|
57
|
-
*
|
|
58
|
-
* **Not** applied by default: pass it explicitly to opt in:
|
|
59
|
-
*
|
|
60
|
-
* ```tsx
|
|
61
|
-
* <ContactPage
|
|
62
|
-
* …
|
|
63
|
-
* categories={DEFAULT_CONTACT_CATEGORIES}
|
|
64
|
-
* />
|
|
65
|
-
* ```
|
|
66
|
-
*
|
|
67
|
-
* The component default for `categories` is `[]` (no dropdown) so this
|
|
68
|
-
* SDK release is backward-compatible with existing consumers that
|
|
69
|
-
* don't yet know about the field — see the prop docs.
|
|
70
|
-
*/
|
|
71
|
-
declare const DEFAULT_CONTACT_CATEGORIES: ContactCategory[];
|
|
72
39
|
interface ContactPageProps {
|
|
73
40
|
/** Product name. Falls back to SDK config productId if not provided. */
|
|
74
41
|
productName?: string;
|
|
@@ -86,50 +53,8 @@ interface ContactPageProps {
|
|
|
86
53
|
heroTitle?: string;
|
|
87
54
|
/** Hero description */
|
|
88
55
|
heroDescription?: string;
|
|
89
|
-
/**
|
|
90
|
-
* Heading level for the hero title. Defaults to `1` (a full page hero,
|
|
91
|
-
* `<h1>`). Pass `2` when embedding ContactPage inside another page that
|
|
92
|
-
* already owns the page `<h1>` (e.g. `PartnersPage`) — renders a compact
|
|
93
|
-
* `<h2>` section header instead of a competing hero, keeping one `<h1>`
|
|
94
|
-
* per page for SEO and a clean visual hierarchy.
|
|
95
|
-
*/
|
|
96
|
-
headingLevel?: 1 | 2;
|
|
97
56
|
/** Additional contact options */
|
|
98
57
|
additionalOptions?: ContactOption[];
|
|
99
|
-
/**
|
|
100
|
-
* Inquiry-type dropdown allow-list. **Defaults to `[]`** — the
|
|
101
|
-
* dropdown does not render unless the consumer explicitly opts in.
|
|
102
|
-
*
|
|
103
|
-
* Two ways to opt in:
|
|
104
|
-
*
|
|
105
|
-
* ```tsx
|
|
106
|
-
* // 1. Provide a product-specific list
|
|
107
|
-
* <ContactPage categories={[{ value: 'sales', label: 'Sales' }, …]} />
|
|
108
|
-
*
|
|
109
|
-
* // 2. Or use the conservative SDK starter list
|
|
110
|
-
* <ContactPage categories={DEFAULT_CONTACT_CATEGORIES} />
|
|
111
|
-
* ```
|
|
112
|
-
*
|
|
113
|
-
* Default is intentionally empty (not `DEFAULT_CONTACT_CATEGORIES`)
|
|
114
|
-
* to keep this SDK release backward-compatible with existing
|
|
115
|
-
* consumers that don't yet wire `categories` — they get zero
|
|
116
|
-
* behavior change.
|
|
117
|
-
*
|
|
118
|
-
* Typed `readonly` because the component never mutates the array;
|
|
119
|
-
* lets product sites pass an `as const`-frozen config without an
|
|
120
|
-
* extra `[...spread]` at the call site.
|
|
121
|
-
*/
|
|
122
|
-
categories?: readonly ContactCategory[];
|
|
123
|
-
/** Label shown above the category dropdown. */
|
|
124
|
-
categoryLabel?: string;
|
|
125
|
-
/** Placeholder text for the unselected state of the dropdown. */
|
|
126
|
-
categoryPlaceholder?: string;
|
|
127
|
-
/**
|
|
128
|
-
* Whether selecting a category is required to submit. Defaults to
|
|
129
|
-
* `true` — the whole point of the dropdown is routing, so users
|
|
130
|
-
* self-triage instead of pushing that work onto support.
|
|
131
|
-
*/
|
|
132
|
-
categoryRequired?: boolean;
|
|
133
58
|
/** SEO metadata */
|
|
134
59
|
seo?: {
|
|
135
60
|
title?: string;
|
|
@@ -139,6 +64,6 @@ interface ContactPageProps {
|
|
|
139
64
|
url?: string;
|
|
140
65
|
};
|
|
141
66
|
}
|
|
142
|
-
declare function ContactPage({ productName, recaptchaSiteKey, contactEmail, contactPhone, officeAddress, responseTime, heroTitle, heroDescription,
|
|
67
|
+
declare function ContactPage({ productName, recaptchaSiteKey, contactEmail, contactPhone, officeAddress, responseTime, heroTitle, heroDescription, additionalOptions, seo, }: ContactPageProps): react_jsx_runtime.JSX.Element;
|
|
143
68
|
|
|
144
|
-
export {
|
|
69
|
+
export { ContactPage, type ContactPageProps };
|
|
@@ -36,39 +36,6 @@ interface ContactOption {
|
|
|
36
36
|
buttonText: string;
|
|
37
37
|
buttonLink: string;
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
* One entry in the contact-form Category dropdown. Each product website
|
|
41
|
-
* supplies its own allow-list via the `categories` prop so the
|
|
42
|
-
* vocabulary stays product-appropriate (e.g. VibeControls uses
|
|
43
|
-
* `plugin-developer`, a manufacturing site uses `dealer`). The backend
|
|
44
|
-
* accepts whatever `value` string is sent and persists it directly.
|
|
45
|
-
*
|
|
46
|
-
* `value` flows to the backend; `label` is what the user sees.
|
|
47
|
-
*/
|
|
48
|
-
interface ContactCategory {
|
|
49
|
-
/** Stored on the server. Short, kebab-case is conventional. */
|
|
50
|
-
value: string;
|
|
51
|
-
/** Human-readable label shown in the dropdown. */
|
|
52
|
-
label: string;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Conservative starter category list — Burdenoff-generic vocabulary
|
|
56
|
-
* (General / Sales / Support / Partnership / Press / Careers / Security).
|
|
57
|
-
*
|
|
58
|
-
* **Not** applied by default: pass it explicitly to opt in:
|
|
59
|
-
*
|
|
60
|
-
* ```tsx
|
|
61
|
-
* <ContactPage
|
|
62
|
-
* …
|
|
63
|
-
* categories={DEFAULT_CONTACT_CATEGORIES}
|
|
64
|
-
* />
|
|
65
|
-
* ```
|
|
66
|
-
*
|
|
67
|
-
* The component default for `categories` is `[]` (no dropdown) so this
|
|
68
|
-
* SDK release is backward-compatible with existing consumers that
|
|
69
|
-
* don't yet know about the field — see the prop docs.
|
|
70
|
-
*/
|
|
71
|
-
declare const DEFAULT_CONTACT_CATEGORIES: ContactCategory[];
|
|
72
39
|
interface ContactPageProps {
|
|
73
40
|
/** Product name. Falls back to SDK config productId if not provided. */
|
|
74
41
|
productName?: string;
|
|
@@ -86,50 +53,8 @@ interface ContactPageProps {
|
|
|
86
53
|
heroTitle?: string;
|
|
87
54
|
/** Hero description */
|
|
88
55
|
heroDescription?: string;
|
|
89
|
-
/**
|
|
90
|
-
* Heading level for the hero title. Defaults to `1` (a full page hero,
|
|
91
|
-
* `<h1>`). Pass `2` when embedding ContactPage inside another page that
|
|
92
|
-
* already owns the page `<h1>` (e.g. `PartnersPage`) — renders a compact
|
|
93
|
-
* `<h2>` section header instead of a competing hero, keeping one `<h1>`
|
|
94
|
-
* per page for SEO and a clean visual hierarchy.
|
|
95
|
-
*/
|
|
96
|
-
headingLevel?: 1 | 2;
|
|
97
56
|
/** Additional contact options */
|
|
98
57
|
additionalOptions?: ContactOption[];
|
|
99
|
-
/**
|
|
100
|
-
* Inquiry-type dropdown allow-list. **Defaults to `[]`** — the
|
|
101
|
-
* dropdown does not render unless the consumer explicitly opts in.
|
|
102
|
-
*
|
|
103
|
-
* Two ways to opt in:
|
|
104
|
-
*
|
|
105
|
-
* ```tsx
|
|
106
|
-
* // 1. Provide a product-specific list
|
|
107
|
-
* <ContactPage categories={[{ value: 'sales', label: 'Sales' }, …]} />
|
|
108
|
-
*
|
|
109
|
-
* // 2. Or use the conservative SDK starter list
|
|
110
|
-
* <ContactPage categories={DEFAULT_CONTACT_CATEGORIES} />
|
|
111
|
-
* ```
|
|
112
|
-
*
|
|
113
|
-
* Default is intentionally empty (not `DEFAULT_CONTACT_CATEGORIES`)
|
|
114
|
-
* to keep this SDK release backward-compatible with existing
|
|
115
|
-
* consumers that don't yet wire `categories` — they get zero
|
|
116
|
-
* behavior change.
|
|
117
|
-
*
|
|
118
|
-
* Typed `readonly` because the component never mutates the array;
|
|
119
|
-
* lets product sites pass an `as const`-frozen config without an
|
|
120
|
-
* extra `[...spread]` at the call site.
|
|
121
|
-
*/
|
|
122
|
-
categories?: readonly ContactCategory[];
|
|
123
|
-
/** Label shown above the category dropdown. */
|
|
124
|
-
categoryLabel?: string;
|
|
125
|
-
/** Placeholder text for the unselected state of the dropdown. */
|
|
126
|
-
categoryPlaceholder?: string;
|
|
127
|
-
/**
|
|
128
|
-
* Whether selecting a category is required to submit. Defaults to
|
|
129
|
-
* `true` — the whole point of the dropdown is routing, so users
|
|
130
|
-
* self-triage instead of pushing that work onto support.
|
|
131
|
-
*/
|
|
132
|
-
categoryRequired?: boolean;
|
|
133
58
|
/** SEO metadata */
|
|
134
59
|
seo?: {
|
|
135
60
|
title?: string;
|
|
@@ -139,6 +64,6 @@ interface ContactPageProps {
|
|
|
139
64
|
url?: string;
|
|
140
65
|
};
|
|
141
66
|
}
|
|
142
|
-
declare function ContactPage({ productName, recaptchaSiteKey, contactEmail, contactPhone, officeAddress, responseTime, heroTitle, heroDescription,
|
|
67
|
+
declare function ContactPage({ productName, recaptchaSiteKey, contactEmail, contactPhone, officeAddress, responseTime, heroTitle, heroDescription, additionalOptions, seo, }: ContactPageProps): react_jsx_runtime.JSX.Element;
|
|
143
68
|
|
|
144
|
-
export {
|
|
69
|
+
export { ContactPage, type ContactPageProps };
|