@dolard.eu/versiq-widget 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,55 @@
1
+ Versiq Widget SDK — Commercial Source-available License
2
+ ========================================================
3
+
4
+ Copyright (c) 2024-2026 Sébastien Dolard
5
+
6
+ This software is made available for inspection. Source code is published only
7
+ so integrators can verify what runs on their visitors' browsers. Publication
8
+ is NOT an open-source release and does NOT grant rights normally associated
9
+ with such releases.
10
+
11
+ 1. Permitted use
12
+ You may, without separate written agreement:
13
+ (a) Include `dist/widget.umd.js` or `dist/widget.es.js` on websites you
14
+ operate, for the sole purpose of consuming the Versiq backend API
15
+ using a publishable key you have legitimately obtained.
16
+ (b) Hot-link the bundle from public CDNs that mirror npm (e.g. unpkg,
17
+ jsDelivr), provided you do not strip or alter the file.
18
+ (c) Configure the widget at runtime via the public TypeScript surface
19
+ (theme, position, lifecycle handlers, identity hooks, etc.) as
20
+ documented in the README.
21
+
22
+ 2. Prohibited without prior written agreement
23
+ You may not, in whole or in part:
24
+ (a) Modify, fork, or create derivative works of the source or bundle for
25
+ redistribution under any name.
26
+ (b) Reverse-engineer, decompile, or de-obfuscate the bundle, in whole or
27
+ in part, for the purpose of creating a competing SDK, widget, or
28
+ backend.
29
+ (c) Redistribute the bundle, or any derivative, under a different package
30
+ name, namespace, or scope.
31
+ (d) Remove, alter, or obscure copyright notices, attribution, or this
32
+ license file from any copy.
33
+
34
+ 3. Production use
35
+ Use of this software against the Versiq backend in production is subject
36
+ to the Versiq Commercial Terms of Service. The license granted here does
37
+ not by itself authorize production use; a valid commercial agreement
38
+ (typically reflected by an issued publishable key bound to an
39
+ Application) is required.
40
+
41
+ 4. No warranty
42
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
43
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
45
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
49
+
50
+ 5. Termination
51
+ Any breach of section 2 terminates your rights under section 1
52
+ immediately, without notice, and you must cease all use of the software
53
+ and destroy all copies in your possession or control.
54
+
55
+ For commercial licensing or written-agreement inquiries: admin@dolard.eu
package/README.md ADDED
@@ -0,0 +1,401 @@
1
+ # @dolard.eu/versiq-widget
2
+
3
+ Versiq Widget SDK - Embed conversational qualification into your website.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @dolard.eu/versiq-widget
9
+ # or
10
+ pnpm add @dolard.eu/versiq-widget
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ### Script Tag (CDN)
16
+
17
+ Hot-link from any public npm CDN, pinned to a version:
18
+
19
+ ```html
20
+ <script
21
+ src="https://unpkg.com/@dolard.eu/versiq-widget@0.1.0/dist/widget.umd.js"
22
+ data-api-key="pk_your_key"
23
+ data-theme='{"primaryColor":"#3B82F6"}'
24
+ ></script>
25
+ ```
26
+
27
+ Equivalent via jsDelivr:
28
+
29
+ ```html
30
+ <script
31
+ src="https://cdn.jsdelivr.net/npm/@dolard.eu/versiq-widget@0.1.0/dist/widget.umd.js"
32
+ data-api-key="pk_your_key"
33
+ ></script>
34
+ ```
35
+
36
+ Pinning a version avoids breaking changes from later releases. To always follow
37
+ the latest, drop `@0.1.0`.
38
+
39
+ ### Programmatic API
40
+
41
+ ```typescript
42
+ import { createWidget, type VersiqWidget } from "@dolard.eu/versiq-widget";
43
+
44
+ const widget = createWidget({
45
+ apiKey: "pk_your_key",
46
+ position: "bottom-right",
47
+ theme: { primaryColor: "#3B82F6" },
48
+ });
49
+
50
+ // Control the widget
51
+ widget.open();
52
+ widget.close();
53
+ widget.reset();
54
+
55
+ // Cleanup
56
+ widget.destroy();
57
+ ```
58
+
59
+ ## Configuration
60
+
61
+ ### WidgetConfig
62
+
63
+ | Option | Type | Default | Description |
64
+ | ----------- | --------------------------------------------- | ---------------- | -------------------------------------------- |
65
+ | `apiKey` | `string` | **required** | Publishable API key (`pk_*`) for the app |
66
+ | `position` | `"bottom-right" \| "bottom-left" \| "inline"` | `"bottom-right"` | Widget position on the page |
67
+ | `open` | `boolean` | `false` | Initial open state |
68
+ | `container` | `HTMLElement \| string` | - | Container element for inline mode |
69
+ | `baseUrl` | `string` | Production URL | Base URL for the widget embed |
70
+ | `theme` | `ThemeConfig` | - | Custom theme (merged with server-side theme) |
71
+ | `debug` | `boolean` | `false` | Enable debug logging |
72
+ | `email` | `string` | - | Pre-identified user email (HMAC) |
73
+ | `userId` | `string` | - | Host-side user identifier (HMAC) |
74
+ | `userHash` | `string` | - | HMAC-SHA256 of email/userId |
75
+
76
+ ### ThemeConfig
77
+
78
+ | Option | Type | Description |
79
+ | ----------------- | ----------------------------- | ---------------------------------------------------------------------------------------------- |
80
+ | `primaryColor` | `string` | Primary brand color (hex, e.g., `#3B82F6`) |
81
+ | `backgroundColor` | `string` | Background color for the widget container |
82
+ | `textColor` | `string` | Text color |
83
+ | `borderRadius` | `number` | Border radius in pixels |
84
+ | `fontFamily` | `string` | Font family |
85
+ | `colorScheme` | `"light" \| "dark" \| "auto"` | Widget color scheme. `"auto"` follows the visitor's `prefers-color-scheme`. Defaults to light. |
86
+
87
+ ## API Reference
88
+
89
+ ### createWidget(config)
90
+
91
+ Creates a new widget instance.
92
+
93
+ ```typescript
94
+ const widget = createWidget({
95
+ apiKey: "pk_your_key",
96
+ position: "inline",
97
+ container: document.getElementById("widget-container"),
98
+ open: true,
99
+ });
100
+ ```
101
+
102
+ ### VersiqWidget Methods
103
+
104
+ | Method | Description |
105
+ | ----------------------------------------------------- | -------------------------------------------------------- |
106
+ | `open()` | Open the widget |
107
+ | `close()` | Close the widget |
108
+ | `reset()` | Reset the conversation |
109
+ | `setTheme(theme: ThemeConfig)` | Update theme dynamically |
110
+ | `setColorScheme(scheme: "light" \| "dark" \| "auto")` | Sugar over `setTheme({ colorScheme })` for dark-mode UIs |
111
+ | `identify(params)` | Set host-attested identity (HMAC) |
112
+ | `destroy()` | Remove widget and cleanup |
113
+ | `on(event, handler)` | Subscribe to events |
114
+ | `off(event, handler)` | Unsubscribe from events |
115
+
116
+ ### Window API (Script Tag)
117
+
118
+ When using the script tag, the API is available on `window.Versiq`:
119
+
120
+ ```javascript
121
+ window.Versiq.open();
122
+ window.Versiq.close();
123
+ window.Versiq.setTheme({ primaryColor: "#10B981" });
124
+
125
+ // Wire your own dark-mode toggle to the widget:
126
+ window.Versiq.setColorScheme("dark"); // or "light", or "auto"
127
+ ```
128
+
129
+ ## Events
130
+
131
+ Subscribe to widget events to react to user interactions.
132
+
133
+ ```typescript
134
+ widget.on("ready", () => {
135
+ console.log("Widget is ready");
136
+ });
137
+
138
+ widget.on("profile-update", (data) => {
139
+ console.log("Profile updated:", data.profile);
140
+ });
141
+
142
+ widget.on("qualified", (data) => {
143
+ console.log("Lead qualified:", data.profile, "Score:", data.score);
144
+ });
145
+
146
+ widget.on("message", (data) => {
147
+ console.log("New message:", data.message);
148
+ });
149
+
150
+ widget.on("error", (data) => {
151
+ console.error("Widget error:", data.code, data.message);
152
+ });
153
+ ```
154
+
155
+ ### Event Types
156
+
157
+ | Event | Payload | Description |
158
+ | ------------------- | ------------------------------------------- | ----------------------------- |
159
+ | `ready` | - | Widget is loaded and ready |
160
+ | `open` | - | Widget was opened |
161
+ | `close` | - | Widget was closed |
162
+ | `message` | `{ message: WidgetMessage }` | New chat message |
163
+ | `profile-update` | `{ profile: WidgetProfile }` | Profile data was updated |
164
+ | `qualified` | `{ profile: WidgetProfile, score: number }` | Lead qualification complete |
165
+ | `error` | `{ code: string, message: string }` | An error occurred |
166
+ | `quota-warning` | `{ remaining: number, limit: number }` | Lead quota running low |
167
+ | `quota-exceeded` | - | Lead quota exceeded |
168
+ | `identity-verified` | `{ email: string, userId?: string }` | Host identity verified (HMAC) |
169
+
170
+ ## TypeScript
171
+
172
+ The package includes full TypeScript support with exported types:
173
+
174
+ ```typescript
175
+ import type {
176
+ WidgetConfig,
177
+ ThemeConfig,
178
+ VersiqWidget,
179
+ WidgetEventType,
180
+ WidgetProfile,
181
+ WidgetMessage,
182
+ B2BProfile, // B2B-specific (backward compat)
183
+ } from "@dolard.eu/versiq-widget";
184
+ ```
185
+
186
+ ### Profile Types
187
+
188
+ - **`WidgetProfile`** (`Record<string, unknown>`) — Generic profile used in
189
+ events. Shape depends on the vertical (`BuyerProfile` for real-estate,
190
+ `B2BProfile` for b2b-qualification).
191
+ - **`B2BProfile`** — Typed B2B qualification profile (sector, companySize,
192
+ etc.). Kept for backward compatibility.
193
+
194
+ ## Widget Mode (Real-Estate)
195
+
196
+ When using `vertical: "real-estate"`, the widget runs in **qualification mode**:
197
+ Versiq qualifies the buyer through conversation and transmits the profile — no
198
+ property data is needed from the integrator.
199
+
200
+ ### Flow
201
+
202
+ 1. Versiq detects the user type (buyer, seller, etc.)
203
+ 2. Collects criteria: location, budget, property type, surface...
204
+ 3. Emits `profile-update` at each turn with the current profile
205
+ 4. When qualification is complete, emits `versiq:qualified` with the full
206
+ profile
207
+
208
+ ### Integration Example
209
+
210
+ ```typescript
211
+ const widget = createWidget({
212
+ apiKey: "pk_your_real_estate_key",
213
+ position: "inline",
214
+ container: "#chat",
215
+ open: true,
216
+ });
217
+
218
+ // Track profile updates in real-time
219
+ widget.on("profile-update", (data) => {
220
+ console.log("Criteria so far:", data.profile);
221
+ // data.profile: { userType, location, budget, propertyType, ... }
222
+ });
223
+
224
+ // Lead is fully qualified — trigger your own search/CRM
225
+ widget.on("qualified", (data) => {
226
+ console.log("Qualified lead:", data.profile);
227
+ // data.score: 1 (V1: binary — 1 = qualified)
228
+ triggerPropertySearch(data.profile);
229
+ });
230
+ ```
231
+
232
+ ### Data Tools
233
+
234
+ In widget mode, data-dependent tools (searchProperties, getCityStats,
235
+ estimateProperty, etc.) are **automatically excluded**. The integrator's
236
+ platform provides the data; Versiq handles qualification only.
237
+
238
+ ## Display Modes
239
+
240
+ ### Floating (Default)
241
+
242
+ Widget appears as a floating button in the corner of the page.
243
+
244
+ ```typescript
245
+ createWidget({
246
+ apiKey: "pk_your_key",
247
+ position: "bottom-right", // or "bottom-left"
248
+ });
249
+ ```
250
+
251
+ ### Inline
252
+
253
+ Widget is embedded directly into a container element.
254
+
255
+ ```typescript
256
+ createWidget({
257
+ apiKey: "pk_your_key",
258
+ position: "inline",
259
+ container: document.getElementById("chat-container"),
260
+ open: true,
261
+ });
262
+ ```
263
+
264
+ ## React Integration
265
+
266
+ ```tsx
267
+ "use client";
268
+
269
+ import { useEffect, useRef } from "react";
270
+ import { createWidget, type VersiqWidget } from "@dolard.eu/versiq-widget";
271
+
272
+ export function ContactWidget() {
273
+ const widgetRef = useRef<VersiqWidget | null>(null);
274
+ const containerRef = useRef<HTMLDivElement>(null);
275
+
276
+ useEffect(() => {
277
+ if (!containerRef.current || widgetRef.current) return;
278
+
279
+ const widget = createWidget({
280
+ apiKey: "pk_your_key",
281
+ position: "inline",
282
+ container: containerRef.current,
283
+ open: true,
284
+ theme: { primaryColor: "#3B82F6" },
285
+ });
286
+
287
+ widgetRef.current = widget;
288
+
289
+ return () => {
290
+ widgetRef.current?.destroy();
291
+ widgetRef.current = null;
292
+ };
293
+ }, []);
294
+
295
+ return <div ref={containerRef} className="h-[600px]" />;
296
+ }
297
+ ```
298
+
299
+ ## Data Attributes (Script Tag)
300
+
301
+ | Attribute | Maps to | Example |
302
+ | ---------------- | ---------- | --------------------------------------- |
303
+ | `data-api-key` | `apiKey` | `data-api-key="pk_live_abc123"` |
304
+ | `data-position` | `position` | `data-position="bottom-left"` |
305
+ | `data-open` | `open` | `data-open="true"` |
306
+ | `data-theme` | `theme` | `data-theme='{"primaryColor":"#F00"}'` |
307
+ | `data-base-url` | `baseUrl` | `data-base-url="https://app.versiq.io"` |
308
+ | `data-debug` | `debug` | `data-debug="true"` |
309
+ | `data-email` | `email` | `data-email="user@example.com"` |
310
+ | `data-user-id` | `userId` | `data-user-id="usr_123"` |
311
+ | `data-user-hash` | `userHash` | `data-user-hash="<hmac>"` |
312
+
313
+ ## E2E Test Selectors (data-testid)
314
+
315
+ The widget exposes a **stable contract** of `data-testid` attributes for
316
+ end-to-end testing (Playwright, Cypress, etc.). These selectors are guaranteed
317
+ not to change without a major version bump.
318
+
319
+ | Selector | Element | Additional attributes |
320
+ | ------------------- | --------------------- | ---------------------------------- |
321
+ | `widget-root` | Chat container (open) | — |
322
+ | `widget-input` | Message input field | — |
323
+ | `widget-send` | Send button | — |
324
+ | `widget-message` | Message bubble | `data-role="user" \| "assistant"` |
325
+ | `widget-suggestion` | Quick reply chip | `data-index="<N>"` (position) |
326
+ | `widget-cta-button` | Call-to-action button | `data-objective="<objectiveType>"` |
327
+ | `widget-avatar` | Header avatar | — |
328
+
329
+ > Contract source: `apps/app/src/app/widget/embed/components/`. Property card
330
+ > selectors (`widget-property-card`) are not yet exposed (pending #727).
331
+
332
+ ### Example (Playwright)
333
+
334
+ ```ts
335
+ await page.getByTestId("widget-input").fill("Looking for a 2-bedroom in Lyon");
336
+ await page.getByTestId("widget-send").click();
337
+ await expect(
338
+ page.getByTestId("widget-message").filter({ hasText: "Lyon" }),
339
+ ).toBeVisible();
340
+ ```
341
+
342
+ ## Development
343
+
344
+ ```bash
345
+ # Install dependencies
346
+ pnpm install
347
+
348
+ # Development build with watch
349
+ pnpm dev
350
+
351
+ # Production build
352
+ pnpm build
353
+
354
+ # Run tests
355
+ pnpm test
356
+
357
+ # Local testing (serves test.html via HTTP - required for iframe CSP)
358
+ pnpm serve
359
+ # Then open http://localhost:5500/test.html
360
+ ```
361
+
362
+ ## Performance
363
+
364
+ ### Bundle Size
365
+
366
+ | Format | Size (minified) | Size (gzipped) | Limit |
367
+ | ------ | --------------- | -------------- | ----- |
368
+ | UMD | ~63 KB | ~16 KB | 50 KB |
369
+ | ESM | ~89 KB | ~19 KB | - |
370
+
371
+ CI enforces the 50 KB gzipped limit via `size-limit`.
372
+
373
+ ### Largest Contentful Paint (LCP)
374
+
375
+ **Target**: < 1.5s
376
+
377
+ The widget loads as an iframe, so LCP depends on:
378
+
379
+ 1. SDK download (~16 KB gzipped) - typically < 100ms
380
+ 2. Iframe creation - instant
381
+ 3. Embed page load - varies by network
382
+
383
+ **Measured baseline**: 0.14s (localhost, no throttling)
384
+
385
+ ### Integration Time
386
+
387
+ **Target**: < 15 minutes from docs to working widget
388
+
389
+ The Quick Start section provides copy-paste integration in under 5 minutes.
390
+
391
+ ## License
392
+
393
+ Commercial Source-available — see [LICENSE](./LICENSE).
394
+
395
+ This is **not** an open-source release: the source is published so integrators
396
+ can audit what runs in their visitors' browsers, but fork / redistribution /
397
+ competing-SDK use are prohibited without a written agreement. Production use
398
+ against the Versiq backend is governed by the Versiq Commercial Terms of
399
+ Service.
400
+
401
+ For commercial licensing inquiries: admin@dolard.eu
@@ -0,0 +1,6 @@
1
+ import { type ApplicationConfigResponse } from "./types";
2
+ export declare class ConfigFetchError extends Error {
3
+ readonly code: string;
4
+ constructor(message: string, code: string);
5
+ }
6
+ export declare function fetchApplicationConfig(apiKey: string, baseUrl: string, externalSignal?: AbortSignal): Promise<ApplicationConfigResponse>;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Versiq Widget SDK
3
+ *
4
+ * Entry point for the widget SDK. Supports both auto-initialization from
5
+ * script tags and programmatic usage.
6
+ *
7
+ * Auto-init usage (canonical integrator surface — #726):
8
+ * ```html
9
+ * <script
10
+ * src="https://cdn.versiq.io/widget.js"
11
+ * data-api-key="pk_live_xxx">
12
+ * </script>
13
+ * ```
14
+ *
15
+ * The integrator's HTML carries only the publishable key (plus optional
16
+ * host-context attributes like `data-container` / `data-debug` / identity
17
+ * fields). Theme, position, language, showProfile, and the default open
18
+ * state are configured from the admin portal (`/portal/widget`) and
19
+ * resolved at load time via the publishable key — editing them from
20
+ * HTML is no longer supported. The legacy `data-theme` / `data-position` /
21
+ * `data-language` / `data-show-profile` / `data-open` attributes are
22
+ * silently ignored.
23
+ *
24
+ * Programmatic usage (internal dev surface — admin preview, marketing
25
+ * demo — not recommended for partner integrations):
26
+ * ```javascript
27
+ * import { createWidget } from '@dolard.eu/versiq-widget';
28
+ *
29
+ * const widget = createWidget({ apiKey: 'pk_live_xxx' });
30
+ * widget.on('qualified', (data) => sendToAnalytics(data));
31
+ * ```
32
+ *
33
+ * Programmatic `theme` / `position` / ... overrides are preserved for the
34
+ * admin sandbox's live-preview use case (client-wins merge). Partner
35
+ * integrators should configure those fields from the admin portal.
36
+ */
37
+ import { VersiqWidget, createWidget } from "./loader";
38
+ import type { WidgetConfig } from "./types";
39
+ export { createWidget, VersiqWidget };
40
+ export { parseScriptAttributes as __parseScriptAttributes };
41
+ export { getCurrentScript as __getCurrentScript };
42
+ export { autoInit as __autoInit };
43
+ export { themeConfigSchema, widgetPositionSchema, widgetConfigSchema, b2bProfileSchema, widgetMessageSchema, applicationConfigResponseSchema, } from "./types";
44
+ export type { WidgetConfig, WidgetPosition, ColorScheme, ThemeConfig, B2BProfile, WidgetProfile, WidgetMessage, WidgetEvent, WidgetEventType, WidgetEventHandler, VersiqAPI, ApplicationConfigResponse, } from "./types";
45
+ export { fetchApplicationConfig, ConfigFetchError } from "./config";
46
+ /**
47
+ * Parse script tag data attributes to extract widget config (#726).
48
+ *
49
+ * Only host-context attributes are read here — fields that cannot live
50
+ * server-side because they are tied to the integrator's page (DOM
51
+ * selectors, dev flags, HMAC-verified identity). Theme, position,
52
+ * language, showProfile, and the default open state are resolved from
53
+ * the admin portal via `fetchApplicationConfig` in `loader.ts`.
54
+ */
55
+ declare function parseScriptAttributes(script: HTMLScriptElement): WidgetConfig | null;
56
+ /**
57
+ * Find the current script tag.
58
+ */
59
+ declare function getCurrentScript(): HTMLScriptElement | null;
60
+ /**
61
+ * Initialize widgets from script tags with data-api-key attributes.
62
+ * Supports multiple widgets on the same page (multi-instance).
63
+ */
64
+ declare function autoInit(): void;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Widget Loader
3
+ *
4
+ * Creates and manages the widget iframe, handling:
5
+ * - iFrame injection and positioning
6
+ * - postMessage communication
7
+ * - Public API exposure
8
+ */
9
+ import type { WidgetConfig, WidgetEventType, WidgetEventHandler, VersiqAPI, ColorScheme, ThemeConfig } from "./types";
10
+ export declare function __resetInstanceCounter(): void;
11
+ export declare class VersiqWidget implements VersiqAPI {
12
+ readonly version = "0.1.0";
13
+ /**
14
+ * Original config as passed by the caller — preserved separately from
15
+ * `this.config` (which has defaults merged on top) so the server-config
16
+ * merge in `resolveConfig()` can tell "field not set by the caller,
17
+ * use the server value" from "caller explicitly passed a default".
18
+ */
19
+ private originalConfig;
20
+ private config;
21
+ private iframe;
22
+ private container;
23
+ private skeletonButton;
24
+ private isOpen;
25
+ private isReady;
26
+ private isInitialized;
27
+ private isDestroyed;
28
+ private fetchAbortController;
29
+ private _applicationId;
30
+ private vertical;
31
+ private instanceId;
32
+ private pendingCommands;
33
+ private eventHandlers;
34
+ private cleanupListener;
35
+ private debugLog;
36
+ private currentViewportMode;
37
+ private mobileMediaQuery;
38
+ private tabletMediaQuery;
39
+ private debounceTimer;
40
+ private viewportChangeHandler;
41
+ get applicationId(): string;
42
+ constructor(config: WidgetConfig);
43
+ /**
44
+ * Minimal placeholder launcher rendered synchronously before the server
45
+ * config arrives. Uses the default position (bottom-right) unless the
46
+ * caller explicitly provided one. Replaced by the real iframe in
47
+ * `init()` after `resolveConfig()` succeeds.
48
+ */
49
+ private renderSkeleton;
50
+ /**
51
+ * Resolve the widget runtime config from the server (#726). Retries once
52
+ * with a 1s backoff on *transient* failures (network, timeout, 5xx) —
53
+ * 4xx and parse errors are permanent config/setup problems that a
54
+ * retry cannot fix, so we surface them immediately. If both attempts
55
+ * fail, the skeleton stays visible and the widget emits `error` —
56
+ * visible degraded mode rather than a silently absent widget.
57
+ */
58
+ private resolveConfig;
59
+ private isTransientFetchError;
60
+ /**
61
+ * Merge the server-resolved application config into the widget state
62
+ * and promote the skeleton to a fully-initialised iframe.
63
+ *
64
+ * Merge semantics (#726):
65
+ * 1. Fields explicitly passed to `createWidget(...)` win (`originalConfig`).
66
+ * This keeps the admin sandbox's live preview working: unsaved edits
67
+ * stay visible on the preview widget.
68
+ * 2. Otherwise the server's `widget` block (from the publishable key
69
+ * lookup) provides theme/position/language/showProfile/open.
70
+ * 3. Legacy `server.theme` is still merged underneath `server.widget.theme`
71
+ * for backward compatibility with pre-#726 data shapes — it will
72
+ * be removed once all consumers land the new contract.
73
+ */
74
+ private applyServerConfig;
75
+ private flushPendingCommands;
76
+ private init;
77
+ private createContainer;
78
+ private createIframe;
79
+ private buildIframeUrl;
80
+ private setupViewportListener;
81
+ private handleViewportChange;
82
+ private getViewportMode;
83
+ private updateContainerForViewport;
84
+ private handleMessage;
85
+ private sendConfig;
86
+ private updateVisibility;
87
+ private emit;
88
+ open(): void;
89
+ close(): void;
90
+ reset(): void;
91
+ identify(params: {
92
+ email: string;
93
+ userId?: string;
94
+ userHash: string;
95
+ }): void;
96
+ setTheme(theme: ThemeConfig): void;
97
+ setColorScheme(scheme: ColorScheme): void;
98
+ on<T = unknown>(event: WidgetEventType, handler: WidgetEventHandler<T>): void;
99
+ off<T = unknown>(event: WidgetEventType, handler: WidgetEventHandler<T>): void;
100
+ destroy(): void;
101
+ private log;
102
+ }
103
+ /**
104
+ * Create a new widget instance.
105
+ */
106
+ export declare function createWidget(config: WidgetConfig): VersiqWidget;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Widget Logger
3
+ *
4
+ * Lightweight logger for the widget SDK with consistent prefix.
5
+ * Provides a centralized logging interface for debug, info, warn, and error levels.
6
+ */
7
+ /**
8
+ * Log an error message.
9
+ */
10
+ export declare function logError(msg: string, ...args: unknown[]): void;
11
+ /**
12
+ * Log a warning message.
13
+ */
14
+ export declare function logWarn(msg: string, ...args: unknown[]): void;
15
+ /**
16
+ * Log an info message.
17
+ */
18
+ export declare function logInfo(msg: string, ...args: unknown[]): void;
19
+ /**
20
+ * Log a debug message.
21
+ */
22
+ export declare function logDebug(msg: string, ...args: unknown[]): void;
23
+ /**
24
+ * Create a conditional debug logger based on debug flag.
25
+ */
26
+ export declare function createDebugLogger(enabled: boolean): typeof logDebug;