@burdenoff/website-sdk 2026.826.1 → 2026.828.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.
Files changed (92) hide show
  1. package/README.md +88 -0
  2. package/dist/client/index.d.mts +2 -2
  3. package/dist/client/index.d.ts +2 -2
  4. package/dist/client/index.js +13 -7
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/client/index.mjs +13 -7
  7. package/dist/client/index.mjs.map +1 -1
  8. package/dist/components/contact.js +13 -7
  9. package/dist/components/contact.js.map +1 -1
  10. package/dist/components/contact.mjs +13 -7
  11. package/dist/components/contact.mjs.map +1 -1
  12. package/dist/components/explore-cta.d.mts +26 -0
  13. package/dist/components/explore-cta.d.ts +26 -0
  14. package/dist/components/explore-cta.js +95 -0
  15. package/dist/components/explore-cta.js.map +1 -0
  16. package/dist/components/explore-cta.mjs +93 -0
  17. package/dist/components/explore-cta.mjs.map +1 -0
  18. package/dist/components/explore.d.mts +43 -0
  19. package/dist/components/explore.d.ts +43 -0
  20. package/dist/components/explore.js +2219 -0
  21. package/dist/components/explore.js.map +1 -0
  22. package/dist/components/explore.mjs +2193 -0
  23. package/dist/components/explore.mjs.map +1 -0
  24. package/dist/components/newsletter.js +13 -7
  25. package/dist/components/newsletter.js.map +1 -1
  26. package/dist/components/newsletter.mjs +13 -7
  27. package/dist/components/newsletter.mjs.map +1 -1
  28. package/dist/components/partners.js +13 -7
  29. package/dist/components/partners.js.map +1 -1
  30. package/dist/components/partners.mjs +13 -7
  31. package/dist/components/partners.mjs.map +1 -1
  32. package/dist/components/pricing.js +13 -7
  33. package/dist/components/pricing.js.map +1 -1
  34. package/dist/components/pricing.mjs +13 -7
  35. package/dist/components/pricing.mjs.map +1 -1
  36. package/dist/components/resource-links.js +13 -7
  37. package/dist/components/resource-links.js.map +1 -1
  38. package/dist/components/resource-links.mjs +13 -7
  39. package/dist/components/resource-links.mjs.map +1 -1
  40. package/dist/components/social-links.js +13 -7
  41. package/dist/components/social-links.js.map +1 -1
  42. package/dist/components/social-links.mjs +13 -7
  43. package/dist/components/social-links.mjs.map +1 -1
  44. package/dist/components/unsubscribe.js +13 -7
  45. package/dist/components/unsubscribe.js.map +1 -1
  46. package/dist/components/unsubscribe.mjs +13 -7
  47. package/dist/components/unsubscribe.mjs.map +1 -1
  48. package/dist/components/waitlist.js +13 -7
  49. package/dist/components/waitlist.js.map +1 -1
  50. package/dist/components/waitlist.mjs +13 -7
  51. package/dist/components/waitlist.mjs.map +1 -1
  52. package/dist/content/index.d.mts +2 -2
  53. package/dist/content/index.d.ts +2 -2
  54. package/dist/content/index.js +13 -7
  55. package/dist/content/index.js.map +1 -1
  56. package/dist/content/index.mjs +13 -7
  57. package/dist/content/index.mjs.map +1 -1
  58. package/dist/{graphql-client-Cv6TO6hn.d.mts → graphql-client-BXn6RYV0.d.mts} +31 -4
  59. package/dist/{graphql-client-Cv6TO6hn.d.ts → graphql-client-BXn6RYV0.d.ts} +31 -4
  60. package/dist/hooks/index.d.mts +1 -0
  61. package/dist/hooks/index.d.ts +1 -0
  62. package/dist/hooks/index.js +1145 -0
  63. package/dist/hooks/index.js.map +1 -0
  64. package/dist/hooks/index.mjs +1140 -0
  65. package/dist/hooks/index.mjs.map +1 -0
  66. package/dist/{index-Cts2CAMO.d.ts → index-BXg7cQLm.d.ts} +1 -1
  67. package/dist/index-CUJTTlYx.d.mts +244 -0
  68. package/dist/index-CUJTTlYx.d.ts +244 -0
  69. package/dist/{index-B36goDSY.d.mts → index-DYxQiEm5.d.mts} +1 -1
  70. package/dist/index.d.mts +8 -5
  71. package/dist/index.d.ts +8 -5
  72. package/dist/index.js +1987 -426
  73. package/dist/index.js.map +1 -1
  74. package/dist/index.mjs +1977 -428
  75. package/dist/index.mjs.map +1 -1
  76. package/dist/product/index.js +13 -7
  77. package/dist/product/index.js.map +1 -1
  78. package/dist/product/index.mjs +13 -7
  79. package/dist/product/index.mjs.map +1 -1
  80. package/dist/{provider-BZlnZDBb.d.mts → provider-CUfI8meE.d.mts} +1 -1
  81. package/dist/{provider-CRLmsW5h.d.ts → provider-D_uUM7_e.d.ts} +1 -1
  82. package/dist/store/index.js +13 -7
  83. package/dist/store/index.js.map +1 -1
  84. package/dist/store/index.mjs +13 -7
  85. package/dist/store/index.mjs.map +1 -1
  86. package/dist/utils/index.d.mts +49 -1
  87. package/dist/utils/index.d.ts +49 -1
  88. package/dist/utils/index.js +70 -0
  89. package/dist/utils/index.js.map +1 -1
  90. package/dist/utils/index.mjs +68 -1
  91. package/dist/utils/index.mjs.map +1 -1
  92. package/package.json +6 -1
package/README.md CHANGED
@@ -108,6 +108,94 @@ function Pricing() {
108
108
  }
109
109
  ```
110
110
 
111
+ ### Explore (`/explore` AI chat)
112
+
113
+ `ExplorePage` is the public AI chat surface: a visitor asks a question in plain
114
+ English and gets an answer drawn from the product's own published documentation,
115
+ with the source pages and next-step CTAs attached.
116
+
117
+ > **Import from the package ROOT.** `ExplorePage`, `ExploreCta` and
118
+ > `useExploreChat` **must** be imported from `@burdenoff/website-sdk`, never
119
+ > from `@burdenoff/website-sdk/components/explore`. This package builds with
120
+ > tsup `splitting: false`, so every sub-path entry bundles **its own copy of the
121
+ > React context** — a sub-path `ExplorePage` cannot see your `WebSDKProvider`
122
+ > and throws `useWebSDK must be used within a WebSDKProvider` even when it is
123
+ > rendered inside one. The sub-path entries exist only for build parity with the
124
+ > other components.
125
+
126
+ ```tsx
127
+ import { ExplorePage, ExploreCta } from '@burdenoff/website-sdk'
128
+ import { useNavigate, useLocation } from 'react-router'
129
+
130
+ function Explore() {
131
+ const navigate = useNavigate()
132
+ return (
133
+ <ExplorePage
134
+ productSlug="vibecontrols"
135
+ heightMode="viewport"
136
+ viewportOffset="4rem" // subtract your site header
137
+ onNavigate={(path) => navigate(path)}
138
+ seo={{ url: 'https://vibecontrols.com/explore' }}
139
+ />
140
+ )
141
+ }
142
+
143
+ // Nav entry points
144
+ function Header() {
145
+ const navigate = useNavigate()
146
+ const { pathname } = useLocation()
147
+ return (
148
+ <>
149
+ <ExploreCta variant="header" onNavigate={(h) => navigate(h)} />
150
+ <ExploreCta variant="floating" currentPath={pathname} />
151
+ </>
152
+ )
153
+ }
154
+ ```
155
+
156
+ **`ExplorePage` props**
157
+
158
+ | Prop | Default | What it does |
159
+ |---|---|---|
160
+ | `seo` | – | Renders `PageHead` with these overrides. Omit to manage `<head>` yourself. |
161
+ | `productSlug` | – | The site this page is on. A soft hint to the assistant, not a hard filter. |
162
+ | `examplePrompts` | – | Prompt chips used only when the backend catalog supplies none. |
163
+ | `className` | – | Extra classes on the page root. |
164
+ | `heightMode` | `'viewport'` | `viewport` pins the chat to the screen with an internally scrolling transcript; `auto` lets the page grow (for embedding). |
165
+ | `viewportOffset` | `0` | Chrome to subtract in `viewport` mode. A number is pixels; a string is any CSS length (`'4rem'`). |
166
+ | `onNavigate` | – | SPA navigation for internal CTA paths. Without it, CTAs render as plain links. |
167
+ | `onSend` | – | Fired with the message text each time a send is accepted (analytics). |
168
+ | `welcomeTitle` / `welcomeBody` | catalog values | Override the welcome copy. `welcomeTitle` **is** the page `<h1>`. |
169
+
170
+ **`ExploreCta` props**: `variant` (`'header' \| 'mobile' \| 'floating'`, required),
171
+ `href` (default `/explore`), `label` (default `Ask AI`), `className`,
172
+ `onNavigate`, `onClick`, `currentPath`. The `floating` variant hides itself on
173
+ `/explore`. Pass `currentPath` from your router — the fallback
174
+ (`window.location.pathname` + `popstate`) cannot observe SPA `pushState`
175
+ navigations.
176
+
177
+ **Prerender safety.** The `<h1>` renders on the *first* render from
178
+ `welcomeTitle` or a built-in fallback catalog — never from the `exploreCatalog`
179
+ fetch. The website prerender waits for an `h1`, so a heading behind a network
180
+ round-trip would bake blank static pages. For the same reason a failed catalog
181
+ fetch degrades to the built-in catalog rather than an empty page.
182
+
183
+ **reCAPTCHA v3 is backend-driven.** Nothing to wire on the website: the SDK loads
184
+ the v3 script and mints a token only when `exploreCatalog.captchaRequired &&
185
+ recaptchaSiteKey` come back set. Turning captcha on is a backend secret change
186
+ with **no website redeploy**, and the "protected by reCAPTCHA" notice appears
187
+ with it. This is separate from the v2 checkbox used by `ContactPage`.
188
+
189
+ **Headless use.** `useExploreChat(options)` exposes the whole state machine —
190
+ `{ phase, catalog, messages, activity, remainingToday, error, focusProduct,
191
+ setFocusProduct, send, stop, retry, reset, canSend }` — if you want to build a
192
+ different shell around it.
193
+
194
+ **Testing hooks.** Stable `data-boff-explore` attributes are attached for the
195
+ end-to-end suite: `page`, `messages`, `composer`, `chip`, `thinking`, `user`,
196
+ `assistant` (plus `data-status`), `reference`, `cta` (plus `data-kind`),
197
+ `cta-header`, `cta-mobile`, `cta-floating`, `send`, `stop`, `new-chat`, `error`.
198
+
111
199
  ### SEO Component
112
200
 
113
201
  ```tsx
@@ -1,5 +1,5 @@
1
- export { W as WebSDKClient, a as WebSDKClientConfig } from '../graphql-client-Cv6TO6hn.mjs';
2
- export { W as WebSDKProvider, a as WebSDKProviderProps, u as useWebSDK, b as useWebSDKConfig } from '../provider-BZlnZDBb.mjs';
1
+ export { G as GraphQLResponse, W as WebSDKClient, a as WebSDKClientConfig, b as WebSDKMutateOptions, c as WebSDKQueryOptions } from '../graphql-client-BXn6RYV0.mjs';
2
+ export { W as WebSDKProvider, a as WebSDKProviderProps, u as useWebSDK, b as useWebSDKConfig } from '../provider-CUfI8meE.mjs';
3
3
  import 'react';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
- export { W as WebSDKClient, a as WebSDKClientConfig } from '../graphql-client-Cv6TO6hn.js';
2
- export { W as WebSDKProvider, a as WebSDKProviderProps, u as useWebSDK, b as useWebSDKConfig } from '../provider-CRLmsW5h.js';
1
+ export { G as GraphQLResponse, W as WebSDKClient, a as WebSDKClientConfig, b as WebSDKMutateOptions, c as WebSDKQueryOptions } from '../graphql-client-BXn6RYV0.js';
2
+ export { W as WebSDKProvider, a as WebSDKProviderProps, u as useWebSDK, b as useWebSDKConfig } from '../provider-D_uUM7_e.js';
3
3
  import 'react';
4
4
 
5
5
  /**
@@ -59,9 +59,15 @@ var _WebSDKClient = class _WebSDKClient {
59
59
  return this.config;
60
60
  }
61
61
  /**
62
- * Execute a GraphQL query (with deduplication and caching)
62
+ * Execute a GraphQL query (with deduplication and caching).
63
+ *
64
+ * Pass `{ cache: false }` to bypass the cache entirely — required for poll
65
+ * loops, where every tick must reach the network.
63
66
  */
64
- async query(query, variables) {
67
+ async query(query, variables, options) {
68
+ if (options?.cache === false) {
69
+ return this.request(query, variables, true, options.timeoutMs);
70
+ }
65
71
  const cacheKey = JSON.stringify({ query, variables });
66
72
  const cached = this.queryCache.get(cacheKey);
67
73
  if (cached) {
@@ -74,7 +80,7 @@ var _WebSDKClient = class _WebSDKClient {
74
80
  if (existing) {
75
81
  return existing;
76
82
  }
77
- const promise = this.request(query, variables).then((result) => {
83
+ const promise = this.request(query, variables, true, options?.timeoutMs).then((result) => {
78
84
  if (result.data && !result.errors?.length) {
79
85
  if (this.queryCache.size >= _WebSDKClient.MAX_CACHE_ENTRIES) {
80
86
  const oldestEntry = this.queryCache.keys().next().value;
@@ -94,8 +100,8 @@ var _WebSDKClient = class _WebSDKClient {
94
100
  /**
95
101
  * Execute a GraphQL mutation
96
102
  */
97
- async mutate(mutation, variables) {
98
- return this.request(mutation, variables, false);
103
+ async mutate(mutation, variables, options) {
104
+ return this.request(mutation, variables, false, options?.timeoutMs);
99
105
  }
100
106
  /** HTTP statuses worth another attempt — transient server/edge conditions. */
101
107
  static isRetriableStatus(status) {
@@ -166,8 +172,8 @@ var _WebSDKClient = class _WebSDKClient {
166
172
  clearTimeout(timer);
167
173
  }
168
174
  }
169
- async request(query, variables, retry = true) {
170
- const timeoutMs = this.config.timeoutMs ?? _WebSDKClient.DEFAULT_TIMEOUT_MS;
175
+ async request(query, variables, retry = true, timeoutOverrideMs) {
176
+ const timeoutMs = timeoutOverrideMs ?? this.config.timeoutMs ?? _WebSDKClient.DEFAULT_TIMEOUT_MS;
171
177
  const maxRetries = retry ? this.config.maxRetries ?? _WebSDKClient.DEFAULT_MAX_RETRIES : 0;
172
178
  let last;
173
179
  for (let i = 0; i <= maxRetries; i++) {
@@ -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 * Per-request timeout in ms. Kept below the gateway's operation timeout so a\n * stalled request surfaces as an error the UI can render, instead of leaving\n * a page stuck on loading skeletons forever. Default 12000.\n */\n timeoutMs?: number;\n /**\n * Extra attempts for transient failures (timeout / network / 5xx / 429) on\n * QUERIES ONLY. Mutations are never retried — a retried contact-form or\n * waitlist submit would double-submit. Default 1 (2 attempts total).\n */\n maxRetries?: number;\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 private static DEFAULT_TIMEOUT_MS = 12_000;\n private static DEFAULT_MAX_RETRIES = 1;\n private static RETRY_BASE_DELAY_MS = 400;\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 // retry=false: a retried mutation could double-submit (contact form,\n // waitlist signup, order). Only the timeout applies here.\n return this.request<T>(mutation, variables, false);\n }\n\n /** HTTP statuses worth another attempt — transient server/edge conditions. */\n private static isRetriableStatus(status: number): boolean {\n return status === 429 || status === 408 || (status >= 500 && status < 600);\n }\n\n private static sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Perform the HTTP call with a hard timeout. Every outcome resolves to a\n * GraphQLResponse — callers never hang, so a UI's `loading` state always\n * settles into data or a renderable error.\n */\n private async attempt<T>(\n query: string,\n variables: Record<string, unknown> | undefined,\n timeoutMs: number,\n ): Promise<{ res: GraphQLResponse<T>; retriable: boolean }> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\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 signal: controller.signal,\n });\n\n if (!response.ok) {\n console.error(\n \"[WebSDK] GraphQL request failed\",\n response.status,\n response.statusText,\n );\n return {\n res: {\n errors: [\n {\n message: `HTTP ${response.status}: ${response.statusText}`,\n extensions: { code: `HTTP_${response.status}` },\n },\n ],\n },\n retriable: WebSDKClient.isRetriableStatus(response.status),\n };\n }\n\n return {\n res: (await response.json()) as GraphQLResponse<T>,\n retriable: false,\n };\n } catch (error) {\n const aborted =\n error instanceof Error &&\n (error.name === \"AbortError\" || controller.signal.aborted);\n const message = aborted\n ? `Request timed out after ${timeoutMs}ms`\n : error instanceof Error\n ? error.message\n : \"Network error\";\n console.error(\"[WebSDK] Network error\", message);\n return {\n res: {\n errors: [\n {\n message,\n extensions: { code: aborted ? \"TIMEOUT\" : \"NETWORK_ERROR\" },\n },\n ],\n },\n // Transport-level failures are worth one more try; a genuine outage\n // just fails twice quickly and still renders an error state.\n retriable: true,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n\n private async request<T>(\n query: string,\n variables?: Record<string, unknown>,\n retry = true,\n ): Promise<GraphQLResponse<T>> {\n const timeoutMs = this.config.timeoutMs ?? WebSDKClient.DEFAULT_TIMEOUT_MS;\n const maxRetries = retry\n ? (this.config.maxRetries ?? WebSDKClient.DEFAULT_MAX_RETRIES)\n : 0;\n\n let last: GraphQLResponse<T> | undefined;\n for (let i = 0; i <= maxRetries; i++) {\n const { res, retriable } = await this.attempt<T>(\n query,\n variables,\n timeoutMs,\n );\n if (!retriable || i === maxRetries) return res;\n last = res;\n // Small backoff with jitter so a burst of clients doesn't retry in lockstep.\n await WebSDKClient.sleep(\n WebSDKClient.RETRY_BASE_DELAY_MS * (i + 1) + Math.random() * 200,\n );\n }\n return last as GraphQLResponse<T>;\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 * Per-request timeout in ms. Kept below the gateway's operation timeout so a\n * stalled request surfaces as an error the UI can render, instead of leaving\n * a page stuck on loading skeletons forever. Default 12000.\n */\n timeoutMs?: number;\n /**\n * Extra attempts for transient failures (timeout / network / 5xx / 429) on\n * QUERIES ONLY. Mutations are never retried — a retried contact-form or\n * waitlist submit would double-submit. Default 1 (2 attempts total).\n */\n maxRetries?: number;\n}\n\n/**\n * Per-call overrides for {@link WebSDKClient.query}.\n *\n * The defaults (5-minute response cache + in-flight deduplication) are right\n * for the read-once content queries most SDK components make, and wrong for a\n * poll loop: the second poll of the same `(query, variables)` pair would be\n * served straight from the cache and the UI would freeze on the first\n * response forever. Pass `{ cache: false }` for anything polled.\n */\nexport interface WebSDKQueryOptions {\n /**\n * Use the response cache and in-flight deduplication. Default `true`.\n * `false` skips the cache read, the dedupe map AND the cache write, so the\n * call always hits the network and never poisons a later cached read.\n */\n cache?: boolean;\n /** Override the client-wide request timeout for this call only. */\n timeoutMs?: number;\n}\n\n/** Per-call overrides for {@link WebSDKClient.mutate}. */\nexport interface WebSDKMutateOptions {\n /** Override the client-wide request timeout for this call only. */\n timeoutMs?: number;\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 private static DEFAULT_TIMEOUT_MS = 12_000;\n private static DEFAULT_MAX_RETRIES = 1;\n private static RETRY_BASE_DELAY_MS = 400;\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 * Pass `{ cache: false }` to bypass the cache entirely — required for poll\n * loops, where every tick must reach the network.\n */\n async query<T = Record<string, unknown>>(\n query: string,\n variables?: Record<string, unknown>,\n options?: WebSDKQueryOptions,\n ): Promise<GraphQLResponse<T>> {\n // Uncached path: no cache read, no in-flight dedupe, no cache write.\n // Deliberately bypasses the dedupe map too — two identical polls issued a\n // few hundred ms apart must both hit the network, otherwise the second one\n // resolves with the first one's (already stale) snapshot.\n if (options?.cache === false) {\n return this.request<T>(query, variables, true, options.timeoutMs);\n }\n\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, true, options?.timeoutMs)\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 options?: WebSDKMutateOptions,\n ): Promise<GraphQLResponse<T>> {\n // retry=false: a retried mutation could double-submit (contact form,\n // waitlist signup, order). Only the timeout applies here.\n return this.request<T>(mutation, variables, false, options?.timeoutMs);\n }\n\n /** HTTP statuses worth another attempt — transient server/edge conditions. */\n private static isRetriableStatus(status: number): boolean {\n return status === 429 || status === 408 || (status >= 500 && status < 600);\n }\n\n private static sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Perform the HTTP call with a hard timeout. Every outcome resolves to a\n * GraphQLResponse — callers never hang, so a UI's `loading` state always\n * settles into data or a renderable error.\n */\n private async attempt<T>(\n query: string,\n variables: Record<string, unknown> | undefined,\n timeoutMs: number,\n ): Promise<{ res: GraphQLResponse<T>; retriable: boolean }> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\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 signal: controller.signal,\n });\n\n if (!response.ok) {\n console.error(\n \"[WebSDK] GraphQL request failed\",\n response.status,\n response.statusText,\n );\n return {\n res: {\n errors: [\n {\n message: `HTTP ${response.status}: ${response.statusText}`,\n extensions: { code: `HTTP_${response.status}` },\n },\n ],\n },\n retriable: WebSDKClient.isRetriableStatus(response.status),\n };\n }\n\n return {\n res: (await response.json()) as GraphQLResponse<T>,\n retriable: false,\n };\n } catch (error) {\n const aborted =\n error instanceof Error &&\n (error.name === \"AbortError\" || controller.signal.aborted);\n const message = aborted\n ? `Request timed out after ${timeoutMs}ms`\n : error instanceof Error\n ? error.message\n : \"Network error\";\n console.error(\"[WebSDK] Network error\", message);\n return {\n res: {\n errors: [\n {\n message,\n extensions: { code: aborted ? \"TIMEOUT\" : \"NETWORK_ERROR\" },\n },\n ],\n },\n // Transport-level failures are worth one more try; a genuine outage\n // just fails twice quickly and still renders an error state.\n retriable: true,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n\n private async request<T>(\n query: string,\n variables?: Record<string, unknown>,\n retry = true,\n timeoutOverrideMs?: number,\n ): Promise<GraphQLResponse<T>> {\n const timeoutMs =\n timeoutOverrideMs ??\n this.config.timeoutMs ??\n WebSDKClient.DEFAULT_TIMEOUT_MS;\n const maxRetries = retry\n ? (this.config.maxRetries ?? WebSDKClient.DEFAULT_MAX_RETRIES)\n : 0;\n\n let last: GraphQLResponse<T> | undefined;\n for (let i = 0; i <= maxRetries; i++) {\n const { res, retriable } = await this.attempt<T>(\n query,\n variables,\n timeoutMs,\n );\n if (!retriable || i === maxRetries) return res;\n last = res;\n // Small backoff with jitter so a burst of clients doesn't retry in lockstep.\n await WebSDKClient.sleep(\n WebSDKClient.RETRY_BASE_DELAY_MS * (i + 1) + Math.random() * 200,\n );\n }\n return last as GraphQLResponse<T>;\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"]}
@@ -57,9 +57,15 @@ var _WebSDKClient = class _WebSDKClient {
57
57
  return this.config;
58
58
  }
59
59
  /**
60
- * Execute a GraphQL query (with deduplication and caching)
60
+ * Execute a GraphQL query (with deduplication and caching).
61
+ *
62
+ * Pass `{ cache: false }` to bypass the cache entirely — required for poll
63
+ * loops, where every tick must reach the network.
61
64
  */
62
- async query(query, variables) {
65
+ async query(query, variables, options) {
66
+ if (options?.cache === false) {
67
+ return this.request(query, variables, true, options.timeoutMs);
68
+ }
63
69
  const cacheKey = JSON.stringify({ query, variables });
64
70
  const cached = this.queryCache.get(cacheKey);
65
71
  if (cached) {
@@ -72,7 +78,7 @@ var _WebSDKClient = class _WebSDKClient {
72
78
  if (existing) {
73
79
  return existing;
74
80
  }
75
- const promise = this.request(query, variables).then((result) => {
81
+ const promise = this.request(query, variables, true, options?.timeoutMs).then((result) => {
76
82
  if (result.data && !result.errors?.length) {
77
83
  if (this.queryCache.size >= _WebSDKClient.MAX_CACHE_ENTRIES) {
78
84
  const oldestEntry = this.queryCache.keys().next().value;
@@ -92,8 +98,8 @@ var _WebSDKClient = class _WebSDKClient {
92
98
  /**
93
99
  * Execute a GraphQL mutation
94
100
  */
95
- async mutate(mutation, variables) {
96
- return this.request(mutation, variables, false);
101
+ async mutate(mutation, variables, options) {
102
+ return this.request(mutation, variables, false, options?.timeoutMs);
97
103
  }
98
104
  /** HTTP statuses worth another attempt — transient server/edge conditions. */
99
105
  static isRetriableStatus(status) {
@@ -164,8 +170,8 @@ var _WebSDKClient = class _WebSDKClient {
164
170
  clearTimeout(timer);
165
171
  }
166
172
  }
167
- async request(query, variables, retry = true) {
168
- const timeoutMs = this.config.timeoutMs ?? _WebSDKClient.DEFAULT_TIMEOUT_MS;
173
+ async request(query, variables, retry = true, timeoutOverrideMs) {
174
+ const timeoutMs = timeoutOverrideMs ?? this.config.timeoutMs ?? _WebSDKClient.DEFAULT_TIMEOUT_MS;
169
175
  const maxRetries = retry ? this.config.maxRetries ?? _WebSDKClient.DEFAULT_MAX_RETRIES : 0;
170
176
  let last;
171
177
  for (let i = 0; i <= maxRetries; i++) {
@@ -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 * Per-request timeout in ms. Kept below the gateway's operation timeout so a\n * stalled request surfaces as an error the UI can render, instead of leaving\n * a page stuck on loading skeletons forever. Default 12000.\n */\n timeoutMs?: number;\n /**\n * Extra attempts for transient failures (timeout / network / 5xx / 429) on\n * QUERIES ONLY. Mutations are never retried — a retried contact-form or\n * waitlist submit would double-submit. Default 1 (2 attempts total).\n */\n maxRetries?: number;\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 private static DEFAULT_TIMEOUT_MS = 12_000;\n private static DEFAULT_MAX_RETRIES = 1;\n private static RETRY_BASE_DELAY_MS = 400;\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 // retry=false: a retried mutation could double-submit (contact form,\n // waitlist signup, order). Only the timeout applies here.\n return this.request<T>(mutation, variables, false);\n }\n\n /** HTTP statuses worth another attempt — transient server/edge conditions. */\n private static isRetriableStatus(status: number): boolean {\n return status === 429 || status === 408 || (status >= 500 && status < 600);\n }\n\n private static sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Perform the HTTP call with a hard timeout. Every outcome resolves to a\n * GraphQLResponse — callers never hang, so a UI's `loading` state always\n * settles into data or a renderable error.\n */\n private async attempt<T>(\n query: string,\n variables: Record<string, unknown> | undefined,\n timeoutMs: number,\n ): Promise<{ res: GraphQLResponse<T>; retriable: boolean }> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\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 signal: controller.signal,\n });\n\n if (!response.ok) {\n console.error(\n \"[WebSDK] GraphQL request failed\",\n response.status,\n response.statusText,\n );\n return {\n res: {\n errors: [\n {\n message: `HTTP ${response.status}: ${response.statusText}`,\n extensions: { code: `HTTP_${response.status}` },\n },\n ],\n },\n retriable: WebSDKClient.isRetriableStatus(response.status),\n };\n }\n\n return {\n res: (await response.json()) as GraphQLResponse<T>,\n retriable: false,\n };\n } catch (error) {\n const aborted =\n error instanceof Error &&\n (error.name === \"AbortError\" || controller.signal.aborted);\n const message = aborted\n ? `Request timed out after ${timeoutMs}ms`\n : error instanceof Error\n ? error.message\n : \"Network error\";\n console.error(\"[WebSDK] Network error\", message);\n return {\n res: {\n errors: [\n {\n message,\n extensions: { code: aborted ? \"TIMEOUT\" : \"NETWORK_ERROR\" },\n },\n ],\n },\n // Transport-level failures are worth one more try; a genuine outage\n // just fails twice quickly and still renders an error state.\n retriable: true,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n\n private async request<T>(\n query: string,\n variables?: Record<string, unknown>,\n retry = true,\n ): Promise<GraphQLResponse<T>> {\n const timeoutMs = this.config.timeoutMs ?? WebSDKClient.DEFAULT_TIMEOUT_MS;\n const maxRetries = retry\n ? (this.config.maxRetries ?? WebSDKClient.DEFAULT_MAX_RETRIES)\n : 0;\n\n let last: GraphQLResponse<T> | undefined;\n for (let i = 0; i <= maxRetries; i++) {\n const { res, retriable } = await this.attempt<T>(\n query,\n variables,\n timeoutMs,\n );\n if (!retriable || i === maxRetries) return res;\n last = res;\n // Small backoff with jitter so a burst of clients doesn't retry in lockstep.\n await WebSDKClient.sleep(\n WebSDKClient.RETRY_BASE_DELAY_MS * (i + 1) + Math.random() * 200,\n );\n }\n return last as GraphQLResponse<T>;\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 * Per-request timeout in ms. Kept below the gateway's operation timeout so a\n * stalled request surfaces as an error the UI can render, instead of leaving\n * a page stuck on loading skeletons forever. Default 12000.\n */\n timeoutMs?: number;\n /**\n * Extra attempts for transient failures (timeout / network / 5xx / 429) on\n * QUERIES ONLY. Mutations are never retried — a retried contact-form or\n * waitlist submit would double-submit. Default 1 (2 attempts total).\n */\n maxRetries?: number;\n}\n\n/**\n * Per-call overrides for {@link WebSDKClient.query}.\n *\n * The defaults (5-minute response cache + in-flight deduplication) are right\n * for the read-once content queries most SDK components make, and wrong for a\n * poll loop: the second poll of the same `(query, variables)` pair would be\n * served straight from the cache and the UI would freeze on the first\n * response forever. Pass `{ cache: false }` for anything polled.\n */\nexport interface WebSDKQueryOptions {\n /**\n * Use the response cache and in-flight deduplication. Default `true`.\n * `false` skips the cache read, the dedupe map AND the cache write, so the\n * call always hits the network and never poisons a later cached read.\n */\n cache?: boolean;\n /** Override the client-wide request timeout for this call only. */\n timeoutMs?: number;\n}\n\n/** Per-call overrides for {@link WebSDKClient.mutate}. */\nexport interface WebSDKMutateOptions {\n /** Override the client-wide request timeout for this call only. */\n timeoutMs?: number;\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 private static DEFAULT_TIMEOUT_MS = 12_000;\n private static DEFAULT_MAX_RETRIES = 1;\n private static RETRY_BASE_DELAY_MS = 400;\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 * Pass `{ cache: false }` to bypass the cache entirely — required for poll\n * loops, where every tick must reach the network.\n */\n async query<T = Record<string, unknown>>(\n query: string,\n variables?: Record<string, unknown>,\n options?: WebSDKQueryOptions,\n ): Promise<GraphQLResponse<T>> {\n // Uncached path: no cache read, no in-flight dedupe, no cache write.\n // Deliberately bypasses the dedupe map too — two identical polls issued a\n // few hundred ms apart must both hit the network, otherwise the second one\n // resolves with the first one's (already stale) snapshot.\n if (options?.cache === false) {\n return this.request<T>(query, variables, true, options.timeoutMs);\n }\n\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, true, options?.timeoutMs)\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 options?: WebSDKMutateOptions,\n ): Promise<GraphQLResponse<T>> {\n // retry=false: a retried mutation could double-submit (contact form,\n // waitlist signup, order). Only the timeout applies here.\n return this.request<T>(mutation, variables, false, options?.timeoutMs);\n }\n\n /** HTTP statuses worth another attempt — transient server/edge conditions. */\n private static isRetriableStatus(status: number): boolean {\n return status === 429 || status === 408 || (status >= 500 && status < 600);\n }\n\n private static sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Perform the HTTP call with a hard timeout. Every outcome resolves to a\n * GraphQLResponse — callers never hang, so a UI's `loading` state always\n * settles into data or a renderable error.\n */\n private async attempt<T>(\n query: string,\n variables: Record<string, unknown> | undefined,\n timeoutMs: number,\n ): Promise<{ res: GraphQLResponse<T>; retriable: boolean }> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\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 signal: controller.signal,\n });\n\n if (!response.ok) {\n console.error(\n \"[WebSDK] GraphQL request failed\",\n response.status,\n response.statusText,\n );\n return {\n res: {\n errors: [\n {\n message: `HTTP ${response.status}: ${response.statusText}`,\n extensions: { code: `HTTP_${response.status}` },\n },\n ],\n },\n retriable: WebSDKClient.isRetriableStatus(response.status),\n };\n }\n\n return {\n res: (await response.json()) as GraphQLResponse<T>,\n retriable: false,\n };\n } catch (error) {\n const aborted =\n error instanceof Error &&\n (error.name === \"AbortError\" || controller.signal.aborted);\n const message = aborted\n ? `Request timed out after ${timeoutMs}ms`\n : error instanceof Error\n ? error.message\n : \"Network error\";\n console.error(\"[WebSDK] Network error\", message);\n return {\n res: {\n errors: [\n {\n message,\n extensions: { code: aborted ? \"TIMEOUT\" : \"NETWORK_ERROR\" },\n },\n ],\n },\n // Transport-level failures are worth one more try; a genuine outage\n // just fails twice quickly and still renders an error state.\n retriable: true,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n\n private async request<T>(\n query: string,\n variables?: Record<string, unknown>,\n retry = true,\n timeoutOverrideMs?: number,\n ): Promise<GraphQLResponse<T>> {\n const timeoutMs =\n timeoutOverrideMs ??\n this.config.timeoutMs ??\n WebSDKClient.DEFAULT_TIMEOUT_MS;\n const maxRetries = retry\n ? (this.config.maxRetries ?? WebSDKClient.DEFAULT_MAX_RETRIES)\n : 0;\n\n let last: GraphQLResponse<T> | undefined;\n for (let i = 0; i <= maxRetries; i++) {\n const { res, retriable } = await this.attempt<T>(\n query,\n variables,\n timeoutMs,\n );\n if (!retriable || i === maxRetries) return res;\n last = res;\n // Small backoff with jitter so a burst of clients doesn't retry in lockstep.\n await WebSDKClient.sleep(\n WebSDKClient.RETRY_BASE_DELAY_MS * (i + 1) + Math.random() * 200,\n );\n }\n return last as GraphQLResponse<T>;\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"]}
@@ -92,9 +92,15 @@ var _WebSDKClient = class _WebSDKClient {
92
92
  return this.config;
93
93
  }
94
94
  /**
95
- * Execute a GraphQL query (with deduplication and caching)
95
+ * Execute a GraphQL query (with deduplication and caching).
96
+ *
97
+ * Pass `{ cache: false }` to bypass the cache entirely — required for poll
98
+ * loops, where every tick must reach the network.
96
99
  */
97
- async query(query, variables) {
100
+ async query(query, variables, options) {
101
+ if (options?.cache === false) {
102
+ return this.request(query, variables, true, options.timeoutMs);
103
+ }
98
104
  const cacheKey = JSON.stringify({ query, variables });
99
105
  const cached = this.queryCache.get(cacheKey);
100
106
  if (cached) {
@@ -107,7 +113,7 @@ var _WebSDKClient = class _WebSDKClient {
107
113
  if (existing) {
108
114
  return existing;
109
115
  }
110
- const promise = this.request(query, variables).then((result) => {
116
+ const promise = this.request(query, variables, true, options?.timeoutMs).then((result) => {
111
117
  if (result.data && !result.errors?.length) {
112
118
  if (this.queryCache.size >= _WebSDKClient.MAX_CACHE_ENTRIES) {
113
119
  const oldestEntry = this.queryCache.keys().next().value;
@@ -127,8 +133,8 @@ var _WebSDKClient = class _WebSDKClient {
127
133
  /**
128
134
  * Execute a GraphQL mutation
129
135
  */
130
- async mutate(mutation, variables) {
131
- return this.request(mutation, variables, false);
136
+ async mutate(mutation, variables, options) {
137
+ return this.request(mutation, variables, false, options?.timeoutMs);
132
138
  }
133
139
  /** HTTP statuses worth another attempt — transient server/edge conditions. */
134
140
  static isRetriableStatus(status) {
@@ -199,8 +205,8 @@ var _WebSDKClient = class _WebSDKClient {
199
205
  clearTimeout(timer);
200
206
  }
201
207
  }
202
- async request(query, variables, retry = true) {
203
- const timeoutMs = this.config.timeoutMs ?? _WebSDKClient.DEFAULT_TIMEOUT_MS;
208
+ async request(query, variables, retry = true, timeoutOverrideMs) {
209
+ const timeoutMs = timeoutOverrideMs ?? this.config.timeoutMs ?? _WebSDKClient.DEFAULT_TIMEOUT_MS;
204
210
  const maxRetries = retry ? this.config.maxRetries ?? _WebSDKClient.DEFAULT_MAX_RETRIES : 0;
205
211
  let last;
206
212
  for (let i = 0; i <= maxRetries; i++) {