@cshah18/sdk 4.8.0 → 4.9.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/README.md +453 -15
- package/dist/cobuy-sdk.esm.js +6271 -2318
- package/dist/cobuy-sdk.esm.js.map +1 -1
- package/dist/cobuy-sdk.umd.js +4128 -175
- package/dist/cobuy-sdk.umd.js.map +1 -1
- package/dist/stats.html +4949 -0
- package/dist/types/core/analytics.d.ts +8 -4
- package/dist/types/core/api-client.d.ts +392 -38
- package/dist/types/core/auth-strategy.d.ts +7 -7
- package/dist/types/core/cobuy.d.ts +222 -10
- package/dist/types/core/config.d.ts +8 -1
- package/dist/types/core/errors.d.ts +108 -4
- package/dist/types/core/types.d.ts +229 -0
- package/dist/types/core/validation.d.ts +171 -0
- package/dist/types/index.d.ts +1 -2
- package/dist/types/ui/group-list/group-list-modal.d.ts +3 -3
- package/dist/types/ui/lobby/lobby-modal.d.ts +25 -7
- package/dist/types/ui/offline-redemption/offline-redemption-modal.d.ts +2 -2
- package/dist/types/ui/widget/theme.d.ts +127 -9
- package/dist/types/ui/widget/widget-root.d.ts +63 -9
- package/package.json +4 -1
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input validation helpers for SDK configuration and widget options
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Validate product ID format and constraints
|
|
6
|
+
*
|
|
7
|
+
* @param {string} productId - The product ID to validate
|
|
8
|
+
* @returns {boolean} True if product ID is valid, false otherwise
|
|
9
|
+
*
|
|
10
|
+
* @description
|
|
11
|
+
* Validates that a product ID meets the following criteria:
|
|
12
|
+
* - Must be a non-empty string
|
|
13
|
+
* - Maximum length of 200 characters
|
|
14
|
+
* - Cannot be only whitespace
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* validateProductId("prod_123"); // ✅ true
|
|
18
|
+
* validateProductId(""); // ❌ false (empty)
|
|
19
|
+
* validateProductId("x".repeat(201)); // ❌ false (too long)
|
|
20
|
+
*/
|
|
21
|
+
export declare function validateProductId(productId: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Validate CSS color format
|
|
24
|
+
*
|
|
25
|
+
* @param {string} color - The color value to validate
|
|
26
|
+
* @returns {boolean} True if color is valid CSS format, false otherwise
|
|
27
|
+
*
|
|
28
|
+
* @description
|
|
29
|
+
* Validates that a color value is valid CSS format. Supports:
|
|
30
|
+
* - **Hex Colors**: #RGB, #RRGGBB, #RRGGBBAA
|
|
31
|
+
* - **RGB/RGBA**: rgb(255, 0, 0), rgba(255, 0, 0, 0.5)
|
|
32
|
+
* - **HSL/HSLA**: hsl(0, 100%, 50%), hsla(0, 100%, 50%, 0.5)
|
|
33
|
+
* - **Named Colors**: transparent, inherit, initial, unset
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* validateColor("#4f46e5"); // ✅ true (hex)
|
|
37
|
+
* validateColor("#4f46e5cc"); // ✅ true (hex with alpha)
|
|
38
|
+
* validateColor("rgb(0, 0, 0)"); // ✅ true (rgb)
|
|
39
|
+
* validateColor("notacolor"); // ❌ false (invalid)
|
|
40
|
+
*/
|
|
41
|
+
export declare function validateColor(color: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Validate CSS border radius format
|
|
44
|
+
*
|
|
45
|
+
* @param {string} borderRadius - The border radius value to validate
|
|
46
|
+
* @returns {boolean} True if border radius is valid CSS format, false otherwise
|
|
47
|
+
*
|
|
48
|
+
* @description
|
|
49
|
+
* Validates that a border radius value is valid CSS format.
|
|
50
|
+
* Supports common CSS units: px, rem, em, %, ch, ex, vw, vh, vmin, vmax
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* validateBorderRadius("8px"); // ✅ true
|
|
54
|
+
* validateBorderRadius("0.5rem"); // ✅ true
|
|
55
|
+
* validateBorderRadius("8"); // ✅ true (assumes pixels)
|
|
56
|
+
* validateBorderRadius("notsize"); // ❌ false
|
|
57
|
+
*/
|
|
58
|
+
export declare function validateBorderRadius(borderRadius: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Validate CSS font family format
|
|
61
|
+
*
|
|
62
|
+
* @param {string} fontFamily - The font family value to validate
|
|
63
|
+
* @returns {boolean} True if font family is valid, false otherwise
|
|
64
|
+
*
|
|
65
|
+
* @description
|
|
66
|
+
* Validates that a font family value is suitable for use.
|
|
67
|
+
* - Must not be empty or whitespace-only
|
|
68
|
+
* - Maximum length of 500 characters
|
|
69
|
+
* - Can be single font or comma-separated list
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* validateFontFamily("Inter, sans-serif"); // ✅ true
|
|
73
|
+
* validateFontFamily("'Courier New', monospace"); // ✅ true
|
|
74
|
+
* validateFontFamily(""); // ❌ false (empty)
|
|
75
|
+
*/
|
|
76
|
+
export declare function validateFontFamily(fontFamily: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Validate animation speed value
|
|
79
|
+
*
|
|
80
|
+
* @param {string} speed - The animation speed to validate
|
|
81
|
+
* @returns {boolean} True if speed is a valid animation speed value, false otherwise
|
|
82
|
+
*
|
|
83
|
+
* @description
|
|
84
|
+
* Validates that the animation speed is one of the predefined values.
|
|
85
|
+
* Valid values: "none", "fast", "normal", "slow"
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* validateAnimationSpeed("normal"); // ✅ true
|
|
89
|
+
* validateAnimationSpeed("slow"); // ✅ true
|
|
90
|
+
* validateAnimationSpeed("fast"); // ✅ true (case-insensitive)
|
|
91
|
+
* validateAnimationSpeed("SLOW"); // ✅ true
|
|
92
|
+
* validateAnimationSpeed("instant"); // ❌ false (invalid value)
|
|
93
|
+
*/
|
|
94
|
+
export declare function validateAnimationSpeed(speed: string): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Validate animation style value
|
|
97
|
+
*
|
|
98
|
+
* @param {string} style - The animation style to validate
|
|
99
|
+
* @returns {boolean} True if style is a valid animation style value, false otherwise
|
|
100
|
+
*
|
|
101
|
+
* @description
|
|
102
|
+
* Validates that the animation style is one of the predefined values.
|
|
103
|
+
* Valid values: "none", "subtle", "smooth", "bouncy"
|
|
104
|
+
* - *none*: No animation
|
|
105
|
+
* - *subtle*: Fade only (no movement)
|
|
106
|
+
* - *smooth*: Fade + slide animation
|
|
107
|
+
* - *bouncy*: Spring-like bounce effect
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* validateAnimationStyle("subtle"); // ✅ true
|
|
111
|
+
* validateAnimationStyle("SMOOTH"); // ✅ true (case-insensitive)
|
|
112
|
+
* validateAnimationStyle("bouncy"); // ✅ true
|
|
113
|
+
* validateAnimationStyle("custom"); // ❌ false (invalid)
|
|
114
|
+
*/
|
|
115
|
+
export declare function validateAnimationStyle(style: string): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Validate widget container (selector or element)
|
|
118
|
+
*
|
|
119
|
+
* @param {string | HTMLElement} container - CSS selector string or HTMLElement
|
|
120
|
+
* @returns {boolean} True if container is valid, false otherwise
|
|
121
|
+
*
|
|
122
|
+
* @description
|
|
123
|
+
* Validates that a container is either a valid CSS selector string or
|
|
124
|
+
* an actual HTMLElement instance for widget rendering.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* validateContainer("#my-widget"); // ✅ true (valid selector)
|
|
128
|
+
* validateContainer(document.getElementById("target")); // ✅ true (element)
|
|
129
|
+
* validateContainer(""); // ❌ false (empty selector)
|
|
130
|
+
* validateContainer(" "); // ❌ false (whitespace)
|
|
131
|
+
*/
|
|
132
|
+
export declare function validateContainer(container: string | HTMLElement): boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Validate merchant authentication key
|
|
135
|
+
*
|
|
136
|
+
* @param {string} merchantKey - The merchant key to validate
|
|
137
|
+
* @returns {boolean} True if merchant key is valid, false otherwise
|
|
138
|
+
*
|
|
139
|
+
* @description
|
|
140
|
+
* Validates that a merchant key is properly formatted for authentication.
|
|
141
|
+
* - Must be a non-empty string
|
|
142
|
+
* - Maximum length of 500 characters
|
|
143
|
+
* - Cannot be whitespace-only
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* validateMerchantKey("pk_live_abc123xyz"); // ✅ true
|
|
147
|
+
* validateMerchantKey(""); // ❌ false (empty)
|
|
148
|
+
* validateMerchantKey(" "); // ❌ false (whitespace)
|
|
149
|
+
*/
|
|
150
|
+
export declare function validateMerchantKey(merchantKey: string): boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Validate session authentication token
|
|
153
|
+
*
|
|
154
|
+
* @param {string | Function} token - Session token string or function
|
|
155
|
+
* @returns {boolean} True if token is valid, false otherwise
|
|
156
|
+
*
|
|
157
|
+
* @description
|
|
158
|
+
* Validates that a session token is either:
|
|
159
|
+
* - A non-empty string token, OR
|
|
160
|
+
* - A function that returns a string or Promise<string> for async token retrieval
|
|
161
|
+
*
|
|
162
|
+
* This allows both static tokens and dynamic token providers.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* validateSessionToken("token_abc123"); // ✅ true
|
|
166
|
+
* validateSessionToken(() => "dynamic_token"); // ✅ true
|
|
167
|
+
* validateSessionToken(async () => await fetchToken()); // ✅ true
|
|
168
|
+
* validateSessionToken(""); // ❌ false (empty string)
|
|
169
|
+
* validateSessionToken({} as any); // ❌ false (not function)
|
|
170
|
+
*/
|
|
171
|
+
export declare function validateSessionToken(token: string | (() => string | Promise<string>)): boolean;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { CoBuy } from "./core/cobuy";
|
|
2
1
|
import type { CoBuySDK } from "./core/types";
|
|
3
2
|
export type { CoBuySDK };
|
|
4
3
|
export * from "./core/types";
|
|
5
4
|
export type { AuthStrategy } from "./core/auth-strategy";
|
|
6
5
|
export { PublicKeyAuth, TokenAuth } from "./core/auth-strategy";
|
|
7
|
-
declare const instance: CoBuy;
|
|
8
6
|
declare global {
|
|
9
7
|
interface Window {
|
|
10
8
|
CoBuy?: CoBuySDK;
|
|
11
9
|
}
|
|
12
10
|
}
|
|
11
|
+
declare const instance: CoBuySDK;
|
|
13
12
|
export default instance;
|
|
@@ -17,10 +17,10 @@ export interface ApiGroupData extends ProductPrimaryGroupData {
|
|
|
17
17
|
*/
|
|
18
18
|
export declare class GroupListModal {
|
|
19
19
|
private overlayEl;
|
|
20
|
-
private logger;
|
|
20
|
+
private readonly logger;
|
|
21
21
|
private groups;
|
|
22
22
|
private liveCount;
|
|
23
|
-
private apiClient;
|
|
23
|
+
private readonly apiClient;
|
|
24
24
|
private isLoading;
|
|
25
25
|
private startButton;
|
|
26
26
|
private countdownIntervals;
|
|
@@ -46,7 +46,7 @@ export declare class GroupListModal {
|
|
|
46
46
|
/** Unsubscribe from socket events */
|
|
47
47
|
private unsubscribeFromSocketEvents;
|
|
48
48
|
/** Handle group member joined socket event and update groups list */
|
|
49
|
-
private onGroupMemberJoined;
|
|
49
|
+
private readonly onGroupMemberJoined;
|
|
50
50
|
/** Update the rendered group card with new data */
|
|
51
51
|
private updateGroupCard;
|
|
52
52
|
open(productId?: string, sessionId?: string, joinedGroupId?: string): Promise<void>;
|
|
@@ -2,7 +2,6 @@ import { ApiClient } from "../../core/api-client";
|
|
|
2
2
|
import { OfflineRedemption } from "../../core/types";
|
|
3
3
|
import { AnalyticsClient } from "../../core/analytics";
|
|
4
4
|
import { SocketManager } from "../../core/socket";
|
|
5
|
-
import "./styles/styles.css";
|
|
6
5
|
export interface LobbyModalData {
|
|
7
6
|
productId: string;
|
|
8
7
|
groupId?: string;
|
|
@@ -36,19 +35,20 @@ export interface LobbyModalCallbacks {
|
|
|
36
35
|
* LobbyModal - Renders and manages the group buying lobby modal
|
|
37
36
|
*/
|
|
38
37
|
export declare class LobbyModal {
|
|
39
|
-
private logger;
|
|
40
|
-
private analyticsClient;
|
|
41
|
-
private socketManager;
|
|
42
|
-
private apiClient;
|
|
38
|
+
private readonly logger;
|
|
39
|
+
private readonly analyticsClient;
|
|
40
|
+
private readonly socketManager;
|
|
41
|
+
private readonly apiClient;
|
|
43
42
|
private modalElement;
|
|
44
43
|
private data;
|
|
45
|
-
private callbacks;
|
|
44
|
+
private readonly callbacks;
|
|
46
45
|
private timerInterval;
|
|
47
46
|
private activityInterval;
|
|
48
47
|
private activityCards;
|
|
49
48
|
private socketListenerRegistered;
|
|
50
49
|
private currentGroupId;
|
|
51
50
|
private shareOverlay;
|
|
51
|
+
private keyboardHandler;
|
|
52
52
|
constructor(data: LobbyModalData, callbacks: LobbyModalCallbacks, apiClient: ApiClient | null, analyticsClient: AnalyticsClient | null, socketManager?: SocketManager | null, debug?: boolean);
|
|
53
53
|
/**
|
|
54
54
|
* Derive lock state from data so UI reflects completion
|
|
@@ -120,6 +120,12 @@ export declare class LobbyModal {
|
|
|
120
120
|
* Inject styles for offline redemption section
|
|
121
121
|
*/
|
|
122
122
|
private injectOfflineRedemptionStyles;
|
|
123
|
+
/**
|
|
124
|
+
* Inject lobby styles into the document head
|
|
125
|
+
* This is called once when the first lobby modal is created
|
|
126
|
+
*/
|
|
127
|
+
private static stylesInjected;
|
|
128
|
+
private injectLobbyStyles;
|
|
123
129
|
/**
|
|
124
130
|
* Trigger the same checkout flow as clicking "Continue to Checkout" on the widget
|
|
125
131
|
*/
|
|
@@ -240,6 +246,18 @@ export declare class LobbyModal {
|
|
|
240
246
|
* Open/render the modal
|
|
241
247
|
*/
|
|
242
248
|
open(groupId?: string): void;
|
|
249
|
+
/**
|
|
250
|
+
* Set up keyboard accessibility features: ESC to close and Tab focus trap
|
|
251
|
+
*/
|
|
252
|
+
private setupKeyboardAccessibility;
|
|
253
|
+
/**
|
|
254
|
+
* Get all focusable elements within the modal
|
|
255
|
+
*/
|
|
256
|
+
private getFocusableElements;
|
|
257
|
+
/**
|
|
258
|
+
* Clean up keyboard event listeners
|
|
259
|
+
*/
|
|
260
|
+
private removeKeyboardAccessibility;
|
|
243
261
|
/**
|
|
244
262
|
* Close the modal
|
|
245
263
|
*/
|
|
@@ -286,7 +304,7 @@ export declare class LobbyModal {
|
|
|
286
304
|
/**
|
|
287
305
|
* Handle socket group update events
|
|
288
306
|
*/
|
|
289
|
-
private handleSocketGroupUpdate;
|
|
307
|
+
private readonly handleSocketGroupUpdate;
|
|
290
308
|
/**
|
|
291
309
|
* Create activity item from socket event data
|
|
292
310
|
*/
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import { OfflineRedemption } from "../../core/types";
|
|
6
6
|
export declare class OfflineRedemptionModal {
|
|
7
7
|
private modalElement;
|
|
8
|
-
private offlineRedemption;
|
|
9
|
-
private onClose?;
|
|
8
|
+
private readonly offlineRedemption;
|
|
9
|
+
private readonly onClose?;
|
|
10
10
|
constructor(offlineRedemption: OfflineRedemption, onClose?: () => void);
|
|
11
11
|
/**
|
|
12
12
|
* Open the modal
|
|
@@ -1,19 +1,137 @@
|
|
|
1
1
|
import { CoBuyThemeOptions } from "../../core/types";
|
|
2
2
|
/**
|
|
3
|
-
* Merge global theme with
|
|
4
|
-
*
|
|
3
|
+
* Merge global theme with widget-specific theme overrides
|
|
4
|
+
*
|
|
5
|
+
* Combines multiple theme objects with priority: widgetTheme > globalTheme > defaults.
|
|
6
|
+
* Validates all color and style values, falling back to defaults if invalid.
|
|
7
|
+
*
|
|
8
|
+
* @param {CoBuyThemeOptions} [globalTheme={}] - Global theme to apply to all widgets
|
|
9
|
+
* @param {string} [globalTheme.primaryColor] - Primary brand color (validated as CSS color)
|
|
10
|
+
* @param {string} [globalTheme.secondaryColor] - Secondary brand color
|
|
11
|
+
* @param {string} [globalTheme.textColor] - Text color for widget
|
|
12
|
+
* @param {string} [globalTheme.borderRadius] - Border radius for widget elements (CSS size value)
|
|
13
|
+
* @param {string} [globalTheme.fontFamily] - Font family for widget text
|
|
14
|
+
* @param {"none" | "fast" | "normal" | "slow"} [globalTheme.animationSpeed] - Animation speed
|
|
15
|
+
* @param {"none" | "subtle" | "smooth" | "bouncy"} [globalTheme.animationStyle] - Animation style
|
|
16
|
+
*
|
|
17
|
+
* @param {CoBuyThemeOptions} [widgetTheme={}] - Widget-specific theme overrides (takes priority over global)
|
|
18
|
+
* @param {string} [widgetTheme.primaryColor] - Override primary color
|
|
19
|
+
* @param {string} [widgetTheme.secondaryColor] - Override secondary color
|
|
20
|
+
* @param {string} [widgetTheme.textColor] - Override text color
|
|
21
|
+
* @param {string} [widgetTheme.borderRadius] - Override border radius
|
|
22
|
+
* @param {string} [widgetTheme.fontFamily] - Override font family
|
|
23
|
+
* @param {"none" | "fast" | "normal" | "slow"} [widgetTheme.animationSpeed] - Override animation speed
|
|
24
|
+
* @param {"none" | "subtle" | "smooth" | "bouncy"} [widgetTheme.animationStyle] - Override animation style
|
|
25
|
+
*
|
|
26
|
+
* @returns {Required<CoBuyThemeOptions>} Merged theme with all properties defined
|
|
27
|
+
*
|
|
28
|
+
* @description
|
|
29
|
+
* All theme values are validated:
|
|
30
|
+
* - Colors are checked against valid CSS color formats
|
|
31
|
+
* - Border radius is validated as a CSS size value
|
|
32
|
+
* - Font family is validated as a non-empty string
|
|
33
|
+
* - Animation speeds and styles are checked against allowed values
|
|
34
|
+
* - Invalid values are logged and replaced with defaults
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* // Merge global and widget themes
|
|
39
|
+
* const theme = mergeTheme(
|
|
40
|
+
* {
|
|
41
|
+
* primaryColor: '#4f46e5',
|
|
42
|
+
* fontFamily: 'Inter, sans-serif'
|
|
43
|
+
* },
|
|
44
|
+
* {
|
|
45
|
+
* primaryColor: '#ff0000' // Override primary color for this widget
|
|
46
|
+
* }
|
|
47
|
+
* );
|
|
48
|
+
* // Result: theme.primaryColor === '#ff0000'
|
|
49
|
+
*
|
|
50
|
+
* // With invalid color (falls back to default)
|
|
51
|
+
* const theme2 = mergeTheme(
|
|
52
|
+
* { primaryColor: 'notacolor' },
|
|
53
|
+
* {}
|
|
54
|
+
* );
|
|
55
|
+
* // Result: theme2.primaryColor === '#4f46e5' (default value)
|
|
56
|
+
* ```
|
|
5
57
|
*/
|
|
6
58
|
export declare function mergeTheme(globalTheme?: CoBuyThemeOptions, widgetTheme?: CoBuyThemeOptions): Required<CoBuyThemeOptions>;
|
|
7
59
|
/**
|
|
8
|
-
* Apply theme to widget container using
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
60
|
+
* Apply theme to widget container using CSS custom properties
|
|
61
|
+
*
|
|
62
|
+
* Sets scoped CSS variables on the container element to apply theme colors,
|
|
63
|
+
* sizing, fonts, and animations. This approach isolates styles to the widget
|
|
64
|
+
* instance and prevents conflicts with merchant's global styles.
|
|
65
|
+
*
|
|
66
|
+
* @param {HTMLElement} container - The DOM element to apply theme to
|
|
67
|
+
* @param {Required<CoBuyThemeOptions>} theme - The theme configuration to apply
|
|
68
|
+
* @param {string} theme.primaryColor - Primary brand color (applied to --cobuy-primary-color)
|
|
69
|
+
* @param {string} theme.secondaryColor - Secondary color (applied to --cobuy-secondary-color)
|
|
70
|
+
* @param {string} theme.textColor - Text color (applied to --cobuy-text-color)
|
|
71
|
+
* @param {string} theme.borderRadius - Border radius (applied to --cobuy-border-radius)
|
|
72
|
+
* @param {string} theme.fontFamily - Font family (applied to --cobuy-font-family)
|
|
73
|
+
* @param {AnimationSpeed} theme.animationSpeed - Animation speed for transitions
|
|
74
|
+
* @param {AnimationStyle} theme.animationStyle - Animation style/easing
|
|
75
|
+
*
|
|
76
|
+
* @returns {void}
|
|
77
|
+
*
|
|
78
|
+
* @description
|
|
79
|
+
* Sets the following CSS custom properties on the container:
|
|
80
|
+
* - `--cobuy-primary-color`: Primary brand color
|
|
81
|
+
* - `--cobuy-secondary-color`: Secondary color
|
|
82
|
+
* - `--cobuy-text-color`: Text color
|
|
83
|
+
* - `--cobuy-border-radius`: Border radius value
|
|
84
|
+
* - `--cobuy-font-family`: Font family stack
|
|
85
|
+
* - `--cobuy-animation-duration`: Duration in milliseconds
|
|
86
|
+
* - `--cobuy-animation-easing`: CSS easing function
|
|
87
|
+
* - `--cobuy-animation-transform`: CSS transform for animations
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const container = document.getElementById('cobuy-widget');
|
|
92
|
+
* const theme = {
|
|
93
|
+
* primaryColor: '#4f46e5',
|
|
94
|
+
* secondaryColor: '#6366f1',
|
|
95
|
+
* textColor: '#111827',
|
|
96
|
+
* borderRadius: '8px',
|
|
97
|
+
* fontFamily: 'Inter, sans-serif',
|
|
98
|
+
* animationSpeed: 'normal',
|
|
99
|
+
* animationStyle: 'smooth'
|
|
100
|
+
* };
|
|
101
|
+
*
|
|
102
|
+
* applyTheme(container, theme);
|
|
103
|
+
* // Now all widget styles use the applied theme colors and animations
|
|
104
|
+
* ```
|
|
14
105
|
*/
|
|
15
106
|
export declare function applyTheme(container: HTMLElement, theme: Required<CoBuyThemeOptions>): void;
|
|
16
107
|
/**
|
|
17
|
-
* Get the default theme
|
|
108
|
+
* Get the default theme configuration
|
|
109
|
+
*
|
|
110
|
+
* Returns a copy of the default theme used when no custom theme is provided.
|
|
111
|
+
* Useful for documentation, testing, or as a baseline for customization.
|
|
112
|
+
*
|
|
113
|
+
* @returns {Required<CoBuyThemeOptions>} Default theme with all properties defined
|
|
114
|
+
*
|
|
115
|
+
* @description
|
|
116
|
+
* Default values:
|
|
117
|
+
* - Primary Color: #4f46e5 (Indigo-600)
|
|
118
|
+
* - Secondary Color: #6366f1 (Indigo-500)
|
|
119
|
+
* - Text Color: #111827 (Gray-900)
|
|
120
|
+
* - Border Radius: 8px
|
|
121
|
+
* - Font Family: Inter, system-ui, -apple-system, sans-serif
|
|
122
|
+
* - Animation Speed: normal (300ms)
|
|
123
|
+
* - Animation Style: subtle (fade only)
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* const defaults = getDefaultTheme();
|
|
128
|
+
* console.log(defaults.primaryColor); // #4f46e5
|
|
129
|
+
*
|
|
130
|
+
* // Use default as base and customize
|
|
131
|
+
* const customTheme = {
|
|
132
|
+
* ...getDefaultTheme(),
|
|
133
|
+
* primaryColor: '#ff0000'
|
|
134
|
+
* };
|
|
135
|
+
* ```
|
|
18
136
|
*/
|
|
19
137
|
export declare function getDefaultTheme(): Required<CoBuyThemeOptions>;
|
|
@@ -5,17 +5,16 @@ import { AnalyticsClient } from "../../core/analytics";
|
|
|
5
5
|
* WidgetRoot handles rendering of the CoBuy widget into a DOM container
|
|
6
6
|
*/
|
|
7
7
|
export declare class WidgetRoot {
|
|
8
|
-
private logger;
|
|
9
|
-
private apiClient;
|
|
10
|
-
private analyticsClient;
|
|
11
|
-
private config;
|
|
12
|
-
private events?;
|
|
8
|
+
private readonly logger;
|
|
9
|
+
private readonly apiClient;
|
|
10
|
+
private readonly analyticsClient;
|
|
11
|
+
private readonly config;
|
|
12
|
+
private readonly events?;
|
|
13
13
|
private state;
|
|
14
14
|
private retryCount;
|
|
15
|
-
private readonly MAX_RETRIES;
|
|
16
15
|
private currentProductId;
|
|
17
16
|
private currentContainer;
|
|
18
|
-
private debouncedCTAClick;
|
|
17
|
+
private readonly debouncedCTAClick;
|
|
19
18
|
private currentGroupData;
|
|
20
19
|
private groupFulfilled;
|
|
21
20
|
private frozenReward;
|
|
@@ -31,13 +30,25 @@ export declare class WidgetRoot {
|
|
|
31
30
|
private groupExpiryRefreshTriggered;
|
|
32
31
|
private offlineRedemption;
|
|
33
32
|
private offlineRedemptionModal;
|
|
33
|
+
private isRendering;
|
|
34
|
+
private renderPromise;
|
|
35
|
+
private liveRegionAnnouncer;
|
|
36
|
+
private lastRenderedProductId;
|
|
37
|
+
private lastRenderedContainer;
|
|
38
|
+
private renderDebounceTimer;
|
|
39
|
+
private pendingRenderOptions;
|
|
40
|
+
private readonly RENDER_DEBOUNCE_MS;
|
|
41
|
+
/**
|
|
42
|
+
* Get max retries from config (configurable via performance options)
|
|
43
|
+
*/
|
|
44
|
+
private get MAX_RETRIES();
|
|
34
45
|
constructor(config: InternalConfig, apiClient: ApiClient | null, analyticsClient?: AnalyticsClient | null);
|
|
35
46
|
/** Subscribe once to backend socket events routed through the host page */
|
|
36
47
|
private subscribeToSocketEvents;
|
|
37
48
|
/** Handle backend fulfillment notifications */
|
|
38
|
-
private handleGroupFulfilledEvent;
|
|
49
|
+
private readonly handleGroupFulfilledEvent;
|
|
39
50
|
/** Handle realtime group creation/join events by refreshing group UI */
|
|
40
|
-
private handleGroupUpdatedEvent;
|
|
51
|
+
private readonly handleGroupUpdatedEvent;
|
|
41
52
|
/** Fetch latest group data and re-render containers */
|
|
42
53
|
private refreshGroupDataFromRealtime;
|
|
43
54
|
/** Update widget and any external containers after realtime refresh */
|
|
@@ -59,10 +70,45 @@ export declare class WidgetRoot {
|
|
|
59
70
|
private getGroupListModal;
|
|
60
71
|
/**
|
|
61
72
|
* Render the widget into the specified container
|
|
73
|
+
*
|
|
74
|
+
* Asynchronous method that renders the collaborative buying widget.
|
|
75
|
+
* Automatically fetches product data, groups, and set up event listeners.
|
|
76
|
+
* Includes request deduplication to prevent redundant API calls from rapid re-renders
|
|
77
|
+
* (e.g., React component re-rendering).
|
|
78
|
+
*
|
|
79
|
+
* @param {RenderWidgetOptions} options - Widget rendering options
|
|
80
|
+
* @returns {Promise<void>} Rejects if rendering fails
|
|
81
|
+
*
|
|
82
|
+
* @throws {CoBuyRenderError} If container not found or rendering fails
|
|
83
|
+
* @throws {CoBuyValidationError} If productId validation fails
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* try {
|
|
88
|
+
* const widget = new WidgetRoot(config, apiClient, analyticsClient);
|
|
89
|
+
* await widget.render({
|
|
90
|
+
* productId: 'prod_123',
|
|
91
|
+
* container: '#widget-container'
|
|
92
|
+
* });
|
|
93
|
+
* } catch (error) {
|
|
94
|
+
* if (error instanceof CoBuyRenderError) {
|
|
95
|
+
* console.error('Widget render failed:', error.message);
|
|
96
|
+
* }
|
|
97
|
+
* }
|
|
98
|
+
* ```
|
|
62
99
|
*/
|
|
63
100
|
render(options: RenderWidgetOptions): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Queued render that handles concurrent render protection
|
|
103
|
+
*/
|
|
104
|
+
private doQueuedRender;
|
|
105
|
+
private doRender;
|
|
64
106
|
/**
|
|
65
107
|
* Fetch reward data with automatic retry on failure
|
|
108
|
+
*
|
|
109
|
+
* @param productId The product ID to fetch reward for
|
|
110
|
+
* @returns Promise with reward data on success, null on failure
|
|
111
|
+
* @internal State updates (LOADED/ERROR) are handled by the caller
|
|
66
112
|
*/
|
|
67
113
|
private fetchRewardWithRetry;
|
|
68
114
|
/**
|
|
@@ -97,6 +143,14 @@ export declare class WidgetRoot {
|
|
|
97
143
|
* Handle retry button click - reset retry count and re-fetch
|
|
98
144
|
*/
|
|
99
145
|
private handleRetry;
|
|
146
|
+
/**
|
|
147
|
+
* Get or create a live region announcer for screen reader announcements
|
|
148
|
+
*/
|
|
149
|
+
private getOrCreateLiveRegion;
|
|
150
|
+
/**
|
|
151
|
+
* Announce a message to screen readers via live region
|
|
152
|
+
*/
|
|
153
|
+
private announceToScreenReaders;
|
|
100
154
|
private createWidget;
|
|
101
155
|
/**
|
|
102
156
|
* Inject base layout styles for responsive widget rendering
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cshah18/sdk",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.0",
|
|
4
4
|
"description": "CoBuy Embedded SDK for browser JavaScript integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cobuy-sdk.umd.js",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "rollup -c",
|
|
14
|
+
"build:analyze": "ANALYZE=true rollup -c && echo '📊 Bundle analysis: open dist/stats.html'",
|
|
14
15
|
"build:watch": "rollup -c -w",
|
|
15
16
|
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
16
17
|
"lint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix",
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
"author": "CoBuy",
|
|
30
31
|
"license": "MIT",
|
|
31
32
|
"dependencies": {
|
|
33
|
+
"@fingerprintjs/botd": "^2.0.0",
|
|
32
34
|
"socket.io-client": "^4.7.5"
|
|
33
35
|
},
|
|
34
36
|
"devDependencies": {
|
|
@@ -46,6 +48,7 @@
|
|
|
46
48
|
"prettier": "^3.7.4",
|
|
47
49
|
"rollup": "^4.9.4",
|
|
48
50
|
"rollup-plugin-postcss": "^4.0.2",
|
|
51
|
+
"rollup-plugin-visualizer": "^5.12.0",
|
|
49
52
|
"tslib": "^2.6.2",
|
|
50
53
|
"typescript": "^5.3.3"
|
|
51
54
|
}
|