@cshah18/sdk
v5.7.0
Published
CoBuy Embedded SDK for browser JavaScript integration
Downloads
1,503
Readme
CoBuy Embedded SDK
A lightweight, production-ready TypeScript SDK for embedding CoBuy's collaborative purchasing widget in your web applications. The SDK exposes a global window.CoBuy object that can be loaded via a <script> tag.
Features
- 📦 Lightweight: ~7.5 KB (ESM, minified)
- 🎯 Type-Safe: Full TypeScript support with comprehensive type definitions
- 🚀 Easy Integration: Simple
<script>tag integration - ⚙️ Configurable: Support for multiple environments and theme customization
- 🔧 Well-Tested: ESLint + TypeScript strict mode
- 📝 Documented: Comprehensive JSDoc comments and examples
Installation
Via Script Tag (Browser)
<script src="https://cdn.cobuy.app/cobuy-sdk.umd.js"></script>
<script>
window.CoBuy.init({
merchantKey: "your-merchant-key",
env: "production",
});
window.CoBuy.renderWidget({
productId: "prod_123",
container: "#cobuy-widget",
});
</script>Via NPM (for Module Bundlers)
npm install @cobuy/sdkimport CoBuy from "@cobuy/sdk";
CoBuy.init({
merchantKey: "your-merchant-key",
env: "production",
});
CoBuy.renderWidget({
productId: "prod_123",
container: "#cobuy-widget",
});Quick Start
1. Initialize the SDK
CoBuy.init({
merchantKey: "your-unique-merchant-key",
env: "production", // or "local" for localhost:9000
debug: false,
theme: {
primaryColor: "#4f46e5",
textColor: "#1f2937",
},
});2. Render the Widget
CoBuy.renderWidget({
productId: "prod_123",
container: "#widget-container", // CSS selector or HTMLElement
onCheckout: (data) => {
console.log("Checkout initiated:", data);
},
onError: (error) => {
console.error("Widget error:", error);
},
onOpen: () => {
console.log("Widget opened");
},
onClose: () => {
console.log("Widget closed");
},
});API Reference
CoBuy.init(options: CoBuyInitOptions): void
Initializes the SDK with configuration.
Parameters:
interface CoBuyInitOptions {
merchantKey: string; // Required: Your unique merchant key
env?: "production" | "local"; // Default: "production" (use "local" for localhost:9000)
theme?: {
primaryColor?: string; // e.g., "#4f46e5"
secondaryColor?: string;
textColor?: string;
borderRadius?: string;
fontFamily?: string;
};
debug?: boolean; // Default: false
}Example:
CoBuy.init({
merchantKey: "pk_live_abc123xyz",
env: "production",
debug: false,
theme: {
primaryColor: "#4f46e5",
borderRadius: "8px",
},
});CoBuy.renderWidget(options: RenderWidgetOptions): void
Renders the CoBuy widget into a DOM container.
Parameters:
interface RenderWidgetOptions {
productId: string; // Required: The product to display
container: string | HTMLElement; // CSS selector or element
onCheckout?: (data: { groupId: string; productId: string }) => void;
onError?: (error: unknown) => void;
onOpen?: () => void;
onClose?: () => void;
}Example:
CoBuy.renderWidget({
productId: "prod_456",
container: "#cobuy-widget",
onCheckout: (data) => {
console.log(`Group ${data.groupId} checking out product ${data.productId}`);
},
onError: (error) => {
console.error("Checkout failed:", error);
},
});CoBuy.version: string
Returns the SDK version.
console.log(CoBuy.version); // "1.0.0"CoBuy.getApiClient(): ApiClient | null
Returns the initialized API client instance for making direct API calls.
Example:
const apiClient = CoBuy.getApiClient();
if (apiClient) {
const rewardResponse = await apiClient.getProductReward("prod_123");
if (rewardResponse.success) {
console.log("Reward:", rewardResponse.data.reward);
}
}ApiClient.getProductReward(productId: string): Promise<ApiResponse<ProductRewardData>>
Retrieves reward information for a specific product.
Response Structure:
interface ProductRewardData {
productId: string;
reward: {
id: string;
name: string;
type: "percentage" | "fixed" | "points" | "cashback";
value: string;
description: string | null;
remaining_quantity: number;
total_quantity: number;
valid_from: string; // ISO 8601 date
valid_to: string; // ISO 8601 date
status: "active" | "inactive" | "expired";
};
eligibility: {
isEligible: boolean;
reason?: string;
};
}Example:
const apiClient = CoBuy.getApiClient();
const response = await apiClient.getProductReward("prod_123");
if (response.success) {
const { reward, eligibility } = response.data;
if (eligibility.isEligible) {
console.log(`${reward.value}% ${reward.name}`);
console.log(`Valid until: ${reward.valid_to}`);
}
} else {
console.error("Error:", response.error.message);
}API Base URLs
The SDK automatically selects the appropriate API endpoint based on the env parameter:
| Environment | API URL |
| ------------ | ------------------------------- |
| production | https://api.cobuyza.co.za/api |
| local | http://localhost:9000/api |
The backend URLs are managed internally by the SDK for security.
Performance Configuration
The SDK supports optional performance tuning for different network conditions and use cases. Configure these settings when initializing the SDK:
CoBuy.init({
merchantKey: "your-merchant-key",
env: "production",
performance: {
requestTimeout: 30000, // Default: 30000ms (30 seconds)
maxRetries: 2, // Default: 2
animationSpeed: "normal", // Default: "normal" ("fast" | "normal" | "slow")
},
});Configuration Options
requestTimeout (number, default: 30000)
Maximum time in milliseconds to wait for API requests before timing out.
Use Cases:
- Slow Networks (Mobile, poor connections):
60000or90000 - Fast Networks (Fiber, corporate):
10000or15000 - Standard (Default):
30000
Example - Slow Network:
CoBuy.init({
merchantKey: "your-key",
performance: {
requestTimeout: 60000, // Wait up to 60 seconds for API responses
},
});maxRetries (number, default: 2)
Number of times to automatically retry failed API requests.
Use Cases:
- Flaky Networks (WiFi, variable connectivity):
3or4 - Stable Networks (Fiber, datacenter):
1or2 - Offline-First Apps:
0(handle retries manually)
Example - Flaky Network:
CoBuy.init({
merchantKey: "your-key",
performance: {
maxRetries: 4, // Retry up to 4 times on failure
},
});animationSpeed (string, default: "normal")
Controls the speed of widget animations (open/close, transitions, etc).
Options:
"fast"- Reduced animation duration for snappy interactions"normal"(default) - Standard animation timing"slow"- Extended animation duration for better visibility
Example - Fast Animations:
CoBuy.init({
merchantKey: "your-key",
theme: {
primaryColor: "#4f46e5",
},
performance: {
animationSpeed: "fast",
},
});Complete Example
CoBuy.init({
merchantKey: "pk_live_abc123xyz",
env: "production",
debug: true,
theme: {
primaryColor: "#4f46e5",
borderRadius: "8px",
},
performance: {
requestTimeout: 45000, // 45 seconds for slower connections
maxRetries: 3, // Retry 3 times on failure
animationSpeed: "normal", // Standard animation speed
},
});Default Behavior
If performance config is not provided, the SDK uses sensible defaults optimized for typical production environments:
{
requestTimeout: 30000,
maxRetries: 2,
animationSpeed: "normal"
}Error Handling
The SDK uses a standardized error handling approach for different operation types:
Error Patterns by Method Type
1. Initialization & Validation Methods - Throw synchronous errors
CoBuy.init()- Throws errors for invalid configurationConfigManager.setConfig()- Throws errors for validation failure
2. Widget Rendering - Returns Promise that may reject
CoBuy.renderWidget()- ReturnsPromise<void>that rejects on rendering errors- Can be handled with try/catch OR .catch()
3. API Methods - Return ApiResponse (no exceptions)
apiClient.getProductReward()- Returns{success: true|false, data?, error?}- Check
response.successto determine outcome
Custom Error Classes
CoBuyNotInitializedError
Thrown when attempting to use SDK features before calling CoBuy.init().
try {
CoBuy.renderWidget({
productId: "prod_123",
container: "#widget",
});
} catch (error) {
if (error instanceof CoBuyNotInitializedError) {
console.error("SDK must be initialized first");
CoBuy.init({ merchantKey: "your-key" });
}
}CoBuyInvalidConfigError
Thrown when invalid configuration is provided to CoBuy.init().
try {
CoBuy.init({
authMode: "public",
// Missing required merchantKey
});
} catch (error) {
if (error instanceof CoBuyInvalidConfigError) {
console.error("Config validation failed:", error.message);
console.error("Error code:", error.code); // "INVALID_CONFIG"
}
}CoBuyRenderError
Thrown when widget rendering fails (container not found, DOM issues, etc).
try {
await CoBuy.renderWidget({
productId: "prod_123",
container: "#nonexistent-container",
});
} catch (error) {
if (error instanceof CoBuyRenderError) {
console.error("Widget render failed:", error.message);
console.error("Container selector:", error.details?.selector);
}
}CoBuyValidationError
Thrown when input validation fails (invalid product ID, invalid container selector, etc).
try {
await CoBuy.renderWidget({
productId: "", // Invalid: empty string
container: "#widget",
});
} catch (error) {
if (error instanceof CoBuyValidationError) {
console.error("Input validation failed:", error.message);
console.error("Details:", error.details); // { productId: '' }
}
}CoBuyApiError
Thrown when API communication fails (network errors, CORS issues, API errors, etc).
const apiClient = CoBuy.getApiClient();
try {
const response = await apiClient.getProductReward("prod_123");
if (!response.success) {
// API returned error response - check error details
console.error("API Error:", response.error?.code);
if (response.error?.statusCode === 429) {
console.log("Rate limited - retry after delay");
}
}
} catch (error) {
// This usually doesn't happen as ApiClient returns ApiResponse
// But some methods may throw CoBuyApiError
if (error instanceof CoBuyApiError) {
console.error("API Error:", error.message);
console.error("Code:", error.code);
}
}Error Base Class
All custom errors inherit from CoBuyError which provides consistent structure:
class CoBuyError extends Error {
code: string; // Error code for programmatic handling
details?: unknown; // Additional error details
}Handling Different Error Types
import {
CoBuyError,
CoBuyNotInitializedError,
CoBuyInvalidConfigError,
CoBuyRenderError,
CoBuyValidationError,
CoBuyApiError,
} from "@cobuy/sdk";
// Universal error handler
async function renderWithErrorHandling() {
try {
await CoBuy.renderWidget({
productId: "prod_123",
container: "#widget",
});
} catch (error) {
if (error instanceof CoBuyNotInitializedError) {
// Handle: SDK not initialized
console.error("Please call CoBuy.init() first");
} else if (error instanceof CoBuyInvalidConfigError) {
// Handle: Bad configuration
console.error("Check your init options:", error.message);
} else if (error instanceof CoBuyValidationError) {
// Handle: Invalid input
console.error("Input validation failed:", error.details);
} else if (error instanceof CoBuyRenderError) {
// Handle: Rendering failed
console.error("Failed to render widget:", error.message);
} else if (error instanceof CoBuyApiError) {
// Handle: API communication failed
console.error("API error:", error.code);
} else if (error instanceof CoBuyError) {
// Handle: Other CoBuy errors
console.error("SDK error:", error.code);
} else {
// Handle: Unexpected error
console.error("Unexpected error:", error);
}
}
}📖 Comprehensive Error Reference: See ERROR_CODES.md for complete list of all error codes, handling strategies, and debugging guidance.
Debugging
Enable debug logging to monitor SDK operations:
CoBuy.init({
merchantKey: "your-key",
debug: true, // Enables console logging
});When debug mode is enabled, the SDK logs:
- SDK initialization details
- Environment and API base URL
- Widget rendering operations
Migration Guide
For breaking changes and upgrade steps, see MIGRATION_GUIDE.md.
Versioning & Changelog
This project follows Semantic Versioning and tracks changes in CHANGELOG.md.
Project Structure
cobuy-sdk/
├── src/
│ ├── index.ts # Entry point, window.CoBuy export
│ ├── core/
│ │ ├── cobuy.ts # Main SDK implementation
│ │ ├── config.ts # Configuration manager
│ │ ├── logger.ts # Logging utility
│ │ ├── types.ts # TypeScript type definitions
│ │ └── errors.ts # Custom error classes
│ └── ui/
│ └── widget/
│ └── widget-root.ts # Widget rendering logic
├── dist/ # Compiled output
│ ├── cobuy-sdk.esm.js # ES Module format
│ ├── cobuy-sdk.esm.js.map # Source map
│ ├── cobuy-sdk.umd.js # UMD format (browser globals)
│ └── cobuy-sdk.umd.js.map # Source map
├── package.json
├── tsconfig.json
├── rollup.config.mjs
└── eslint.config.jsDevelopment
Prerequisites
- Node.js 16+
- npm or yarn
Setup
npm installScripts
# Build the SDK
npm run build
# Watch mode (rebuilds on file changes)
npm run build:watch
# Run ESLint
npm run lint
# Fix ESLint issues
npm run lint:fix
# Type check + lint
npm run check
### Versioning (Grunt)
These commands bump the version in package.json, create a commit, and tag the release. Pushing to remote is disabled by default.
```bash
# Patch (x.y.z -> x.y.(z+1))
npm run version:patch
# Minor (x.y.z -> x.(y+1).0)
npm run version:minor
# Major ((x+1).0.0)
npm run version:major
# Prerelease (x.y.z-alpha.0 -> x.y.z-alpha.1)
npm run version:prerelease
# After bumping, push manually if desired
git push && git push --tags
### Code Quality
This project uses:
- **TypeScript**: Strict mode enabled for type safety
- **ESLint**: Enforces code standards
- **Prettier**: Automatic code formatting
- **Rollup**: Module bundler for optimized output
### Building
The SDK is built with Rollup and produces:
1. **ESM Bundle** (`cobuy-sdk.esm.js`): For use with modern module bundlers
2. **UMD Bundle** (`cobuy-sdk.umd.js`): For direct browser inclusion via `<script>` tag
Type definitions are generated automatically and included in the `dist/types/` directory.
## Browser Support
- Modern browsers with ES2017+ support
- Chrome 51+
- Firefox 54+
- Safari 10+
- Edge 15+
## TypeScript Support
The SDK includes full TypeScript definitions. When using with TypeScript:
```typescript
import CoBuy from "@cobuy/sdk";
import type { CoBuySDK, CoBuyInitOptions, RenderWidgetOptions } from "@cobuy/sdk";
// All types are available for import
const options: CoBuyInitOptions = {
merchantKey: "key",
env: "production",
};
CoBuy.init(options);Configuration Examples
Minimal Configuration
CoBuy.init({
merchantKey: "pk_live_123abc",
});Full Configuration
CoBuy.init({
merchantKey: "pk_live_123abc",
env: "production",
theme: {
primaryColor: "#4f46e5",
secondaryColor: "#e5e7eb",
textColor: "#1f2937",
borderRadius: "8px",
fontFamily: "'Inter', sans-serif",
},
debug: true,
});With Event Handlers
CoBuy.renderWidget({
productId: "prod_789",
container: "#my-widget",
onOpen: () => {
// Track analytics
analytics.track("cobuy_widget_opened");
},
onCheckout: (data) => {
// Send to your backend
api.post("/order/initiate", data);
},
onError: (error) => {
// Handle errors gracefully
showErrorNotification(error.message);
},
onClose: () => {
// Cleanup
console.log("Widget closed");
},
});Implementation Status
✅ Complete & Stable
- Core Widget Rendering: Fully implemented with loading states, error handling, and retry logic
- Product Reward Display: Real-time reward data fetching with TTL-based caching
- Group Management: Automatic group creation and joining with progress tracking
- Lobby Modal: Full-featured group coordination UI with sharing, offline redemption, and real-time updates
- Authentication: Support for both public (merchant key) and token-based auth modes
- Theme Customization: Comprehensive theming with colors, animations, and typography
- Accessibility (WCAG AA):
- Keyboard navigation and focus management
- Screen reader support with ARIA labels and live regions
- Semantic HTML and proper heading hierarchy
- Color contrast compliance
- Input Validation: Comprehensive validation for colors, product IDs, and auth credentials
- Request Optimization:
- Response caching with 60s TTL for product rewards
- Request deduplication for concurrent identical calls
- Automatic timeout cleanup to prevent memory leaks
- Debounced group data refreshes
- Error Handling: Custom error classes with descriptive messages
- TypeScript Support: Full type definitions with strict mode enabled
- Browser Compatibility: ES2017+ support (Chrome 51+, Firefox 54+, Safari 10+)
🟡 Partial Implementation
- Offline Redemption: QR code generation and manual code entry supported; SMS/email delivery backend integration pending
Known Limitations
Technical Limitations
- Bundle Size: ~7.5 KB minified/gzipped (unavoidable due to full modal UI + real-time features)
- Single Instance Per Container: Only one widget can render per DOM container; multiple products require multiple containers
- Modal Stacking: Only one modal (lobby or group list) can be open simultaneously
- Network Timeout Guarantees: Hard timeout of 30 seconds on all API calls; no built-in retry for transient 5xx errors
- CSS Scoping: Widget uses CSS custom properties; global CSS may override if specificity is higher
Browser/Platform Limitations
- No IE11 Support: SDK targets ES2017+ only
- WebSocket Required: Real-time features require WebSocket support (unavailable in some corporate networks)
- Third-Party Cookies: Social sharing (WhatsApp, Facebook) requires third-party cookie support
- Offline Mode: Limited; QR code works offline, but group sync requires network connectivity
API/Data Limitations
- Reward Expiry: Expired rewards show as inactive; no pre-expiry notifications (planned for v2.0)
- Group Capacity: Max 999 participants per group (theoretical limit); recommend splitting larger promotions
- Concurrency: Widget serializes renders to prevent race conditions; may add concurrent render support in v2.0
- Rate Limiting: No built-in request rate limiting client-side (server enforces)
Performance Improvements
- Lazy-load modal components (reduce initial bundle by ~2 KB)
- Implement virtual scrolling for activity feeds (currently loads all items)
- Add service worker for offline caching of static assets
Migration Guide
From Older SDK Versions (v0.x → v1.0)
Breaking Changes
API Client Access
v0.x:
const client = window.CoBuy.apiClient;v1.0:
const client = window.CoBuy.getApiClient();Event Callback Names
v0.x:
CoBuy.renderWidget({ onGroupJoined: (groupId) => {}, onCheckoutInitiated: (data) => {}, });v1.0:
CoBuy.renderWidget({ onCheckout: (data) => {}, // Consolidated });Error Handling
v0.x:
try { CoBuy.renderWidget({ /*...*/ }); } catch (error) { if (error.code === "INVALID_CONFIG") { } }v1.0:
try { CoBuy.renderWidget({ /*...*/ }); } catch (error) { if (error instanceof CoBuyInvalidConfigError) { } }
Migration Steps
Update initialization:
// v0.x CoBuy.initialize({ apiKey: "pk_..." }); // v1.0 CoBuy.init({ merchantKey: "pk_..." });Update theme property names:
// v0.x theme: { buttonColor: "#...", darkBg: false, } // v1.0 theme: { primaryColor: "#...", // Dark mode coming in v2.0 }Update error handling:
- Import error classes:
import { CoBuyInvalidConfigError } from "@cobuy/sdk" - Check
error instanceof ErrorClassinstead of error codes
- Import error classes:
Deprecations
These will be removed in v2.0:
- ❌
CoBuy.getRawApiClient()— UseCoBuy.getApiClient()instead - ❌
options.onGroupJoinedcallback — UseonCheckoutinstead
License
MIT
Support
For issues, feature requests, or questions, please contact [email protected] or create an issue in the repository.
Version: 1.0.0 Last Updated: February 2026
