@merittdev/sdk
v0.1.3
Published
SDK for Meritt Engine content management with Firebase integration
Maintainers
Readme
Meritt SDK - Development Context & Architecture
This document provides comprehensive context about the Meritt SDK development, architectural decisions, and implementation details for future reference.
Project Overview
Name: @merittdev/sdk
Purpose: A powerful SDK for Meritt Engine content management with Firebase integration
Architecture: Provider-only React SDK with TypeScript
State Management: Zustand stores
Database: Firebase Firestore
Key Architectural Decisions
1. Provider-Only Architecture (Final Decision)
Decision: Use React Provider pattern exclusively, removing global initializeSDK() function.
Rationale:
- ✅ Cleaner API - just wrap with provider
- ✅ Multiple instances support (multi-tenant apps)
- ✅ Better testing - easy to mock with different configs
- ✅ React-first approach - follows React patterns
- ✅ Type safety - hooks require provider context
- ✅ Clear error handling - helpful errors if provider missing
Implementation:
// Client usage
;<MerittProvider config={firebaseConfig}>
<App />
</MerittProvider>
// Inside components
const { products, loading } = useProducts({ featured: true })Alternative Considered: Hybrid approach with both initializeSDK() and provider, but rejected for complexity.
2. Firebase Instance Management
Decision: Each provider creates its own Firebase app instance with unique names.
Implementation:
- Provider creates Firebase app with namespace:
meritt-${namespace}ormeritt-default - Firebase instances (db, auth, storage) passed to all store methods
- No global Firebase state - everything context-based
Benefits:
- Multiple Firebase projects in same app
- Isolated instances for testing
- No singleton issues
3. Store Architecture
Decision: Zustand stores that accept Firebase instances as parameters.
Pattern:
// Store method signature
fetchProducts: (options: ProductQueryOptions, firebase: FirebaseInstances) =>
Promise<Product[]>
// Hook usage
const { firebase } = useMerittContext()
await fetchProducts(options, firebase)Benefits:
- Works with provider pattern
- Testable with mock Firebase instances
- No global dependencies
4. Firestore Indexes Strategy
Decision: Include firestore.indexes.json file in npm package for manual deployment.
Alternatives Considered:
- ❌ Automated setup script - too complex
- ❌ Manual console creation - too error-prone
- ✅ JSON file + Firebase CLI - simple and standard
Client Setup:
cp node_modules/@merittdev/sdk/firestore.indexes.json .
firebase deploy --only firestore:indexesFile Structure
src/
├── index.ts # Main exports
├── providers/
│ └── MerittProvider.tsx # React provider + context hooks
├── hooks/
│ ├── useProducts.ts # Product management hook
│ ├── useMedia.ts # Media management hook
│ ├── useUpload.ts # File upload hook
│ ├── useSettings.ts # Settings management hook
│ ├── useCategories.ts # Categories management hook
│ └── useCart.ts # Shopping cart hook
├── stores/
│ ├── productStore.ts # Zustand product store
│ ├── mediaStore.ts # Zustand media store
│ ├── settingsStore.ts # Zustand settings store
│ └── cartStore.ts # Zustand cart store
├── services/
│ ├── productService.ts # Direct Firebase product operations
│ └── categoryService.ts # Direct Firebase category operations
├── utils/
│ ├── firebaseContext.ts # Firebase instance utilities
│ ├── contentUtils.ts # Content/Firestore utilities
│ ├── mediaUtils.ts # Media/file utilities
│ ├── formatters.ts # Text and data formatting utilities
│ └── cartUtils.ts # Shopping cart utilities
├── config/
│ └── firebase.ts # Firebase configuration
└── types.ts # TypeScript type definitions
firestore.indexes.json # Firestore composite indexes
package.json # NPM package configuration
README.md # User documentationCore Components
MerittProvider
Purpose: Initialize Firebase and provide context to child components.
Key Features:
- Creates unique Firebase app instances
- Provides Firebase instances to hooks
- Handles initialization state and errors
- Supports multiple instances with namespaces
Exports:
MerittProvider- Main provider componentuseMerittContext()- Access provider contextuseFirebaseInstances()- Direct Firebase access
Content Hooks
Available Hooks:
useProducts(options)- Product managementuseMedia(options)- Media managementuseUpload()- File upload functionalityuseSettings(options)- Settings managementuseCategories(options)- Category managementuseCart(options)- Shopping cart functionality
Pattern:
- All hooks require MerittProvider
- Get Firebase instances from context
- Pass instances to store methods
- Handle loading/error states
Zustand Stores
Architecture:
- Store methods accept Firebase instances as parameters
- No global Firebase dependencies
- Maintain local state (products, loading, error)
- Provide CRUD operations
Example Store Method:
fetchProducts: async (
options: ProductQueryOptions,
firebase: FirebaseInstances,
) => {
// Use firebase.db for Firestore operations
// Update store state
// Return results
}Type System
Core Types
Content Types:
Product- E-commerce productArticle- Blog articlePortfolioProject- Portfolio projectMedia- Media assetCategory- Content categoryOrder- E-commerce orderCart- Shopping cartCartItem- Shopping cart item
Query Types:
ProductQueryOptions- Product filtering optionsMediaQueryOptions- Media filtering optionsArticleQueryOptions- Article filtering optionsPortfolioProjectQueryOptions- Portfolio project filtering optionsCartQueryOptions- Cart filtering optionsSettingsQueryOptions- Settings filtering options
Firebase Types:
FirebaseInstances- Firebase service instancesMerittConfig- Firebase configuration
Project Generator Types:
ProjectConfiguration- Complete project configurationProjectMetadata- Project metadataPageDefinition- Page definitionComponentPlacement- Component placementSectionSelection- Section selectionThemeConfiguration- Theme configurationSiteConfiguration- Site configurationComponentMapping- Component mappingGenerationOptions- Generation optionsGeneratedFile- Generated fileGenerationResult- Generation resultGenerationSummary- Generation summaryTemplateContext- Template contextComponentInstance- Component instance
Firestore Schema
Collections
Products Collection:
{
id: string;
name: string;
slug: string;
price: number;
salePrice?: number;
description: string;
shortDescription?: string;
images: string[];
categories: string[];
tags?: string[];
inStock: boolean;
quantity?: number;
variants?: ProductVariant[];
attributes?: Record<string, string>;
featured?: boolean;
relatedProducts?: string[];
createdAt: Timestamp;
updatedAt: Timestamp;
}Articles Collection:
{
id: string;
title: string;
slug: string;
content: string;
excerpt?: string;
coverImage?: string;
author: string;
publishedDate: Timestamp;
categories: string[];
tags?: string[];
isPublished: boolean;
isFeatured?: boolean;
readTime?: number;
createdAt: Timestamp;
updatedAt: Timestamp;
}Media Collection:
{
id: string;
fileName: string;
fileType: string;
fileSize: number;
url: string;
thumbnailUrl?: string;
alt?: string;
caption?: string;
width?: number;
height?: number;
folder?: string;
tags?: string[];
createdAt: Timestamp;
updatedAt: Timestamp;
}Categories Collection:
{
id: string;
name: string;
slug: string;
description?: string;
parentId?: string;
image?: string;
createdAt: Timestamp;
updatedAt: Timestamp;
}Carts Collection:
{
id: string;
userId?: string;
sessionId?: string;
items: CartItem[];
subtotal: number;
itemCount: number;
discount: number;
discountCode?: string;
currency: string;
createdAt: Timestamp;
updatedAt: Timestamp;
expiresAt?: Timestamp;
}Settings Collection:
{
id: string;
theme: ThemeConfig;
sidebarCollapsed: boolean;
language: string;
timezone: string;
branding?: BrandingConfig;
ui?: UIPreferences;
localization?: LocalizationPreferences;
notifications?: NotificationPreferences;
security?: SecurityPreferences;
projectInfo?: ProjectInfo;
}Required Indexes
Products:
featured + createdAtinStock + createdAtfeatured + inStock + createdAtcategories (array-contains) + createdAtcategories (array-contains) + featured + createdAt
Articles:
isPublished + createdAtisFeatured + createdAtcategories (array-contains) + isPublished + createdAt
Media:
fileType + createdAtfolder + createdAttags (array-contains) + createdAt
Carts:
userId + updatedAtsessionId + updatedAtexpiresAt + updatedAt
Development Decisions Log
Provider vs Initialize Function Debate
Initial Approach: Global initializeSDK() function
Problem: Not React-friendly, no multiple instances, testing issues
Solution: Provider-only architecture
Result: Cleaner, more flexible, better DX
Store Parameter Passing
Challenge: How to get Firebase instances to stores? Options:
- Global Firebase instances (rejected - not provider-friendly)
- Store methods accept Firebase parameters (chosen)
- Context-aware stores (rejected - too complex)
Implementation: All store methods accept firebase: FirebaseInstances parameter
Index Management Strategy
Challenge: How to handle Firestore composite indexes? Options:
- Automated script with deployment (rejected - too complex)
- Manual console creation (rejected - error-prone)
- JSON file + Firebase CLI (chosen - simple, standard)
Result: Include firestore.indexes.json in package, document copy+deploy process
Shopping Cart Implementation
Challenge: How to handle guest carts vs user carts? Solution:
- Use
userIdfor logged-in users - Use
sessionIdfor guest users - Merge carts on login
Implementation:
- Flexible cart store with session management
- Cart persistence in Firestore
- Cart expiration for guest carts
Error Handling Philosophy
Approach: Fail fast with helpful error messages Examples:
- Hook used outside provider: Clear error with setup instructions
- Firebase not initialized: Specific error about provider setup
- Missing indexes: Point to documentation
Testing Strategy
Local Development
Setup:
npm link # In SDK directory
cd /path/to/test-project
npm link @merittdev/sdk # In test projectTest Pattern:
<MerittProvider config={testFirebaseConfig}>
<TestComponent />
</MerittProvider>Multiple Instances Testing
<MerittProvider config={tenant1Config} namespace="tenant1">
<TenantApp />
</MerittProvider>
<MerittProvider config={tenant2Config} namespace="tenant2">
<TenantApp />
</MerittProvider>Performance Considerations
Query Optimization
- All queries use composite indexes
- Pagination with
startAftercursors - Limit queries to reasonable sizes
- Cache results in Zustand stores
Firebase Optimization
- Unique app names prevent conflicts
- Minimal Firebase SDK imports
- Efficient query patterns
Project Generator
The SDK includes a powerful project generator for creating new Meritt Engine projects.
Key Features:
- Dynamic page generation based on project configuration
- Component selection and customization
- Theme configuration
- Firebase integration
- TypeScript type safety
Generation Process:
- Load project configuration from Firestore
- Process component selections and customizations
- Generate files based on templates
- Apply customizations to components
- Set up Firebase configuration
- Generate necessary configuration files
E-commerce Features
The SDK provides comprehensive e-commerce functionality:
Product Management:
- Product CRUD operations
- Product variants
- Product categories
- Product search and filtering
Shopping Cart:
- Add to cart
- Update cart items
- Remove from cart
- Cart persistence
- Guest carts
- Cart merging on login
- Cart expiration
Order Processing:
- Order creation
- Order status management
- Order history
Future Considerations
Potential Enhancements
- Offline Support: Add Firebase offline persistence
- Real-time Updates: WebSocket/Firestore listeners
- Caching Strategy: More sophisticated caching
- Authentication: Built-in auth hooks
- File Upload: Progress tracking, resumable uploads
- Analytics Integration: User behavior tracking
- Payment Processing: Integration with payment gateways
- Internationalization: Multi-language support
- Advanced Search: Full-text search capabilities
- Webhooks: Event-driven integrations
Breaking Changes to Avoid
- Don't change store method signatures
- Don't remove provider pattern
- Don't change core type definitions
- Don't modify index requirements without migration
Troubleshooting Guide
Common Issues
"useMerittContext must be used within a MerittProvider"
- Solution: Wrap app with
<MerittProvider>
"Firebase not initialized"
- Solution: Check provider config, ensure valid Firebase credentials
"Index not found" errors
- Solution: Deploy
firestore.indexes.jsonfile
Products not loading
- Check: Firebase config, indexes deployed, documents exist, console errors
Debug Steps
- Check provider setup and config
- Verify Firebase project access
- Confirm indexes are built
- Check browser console for errors
- Verify document structure matches types
Deployment Checklist
Before Publishing
- [ ] Run
npm run build - [ ] Test with
npm linkin real project - [ ] Verify
firestore.indexes.jsonincluded - [ ] Update version in
package.json - [ ] Update README if needed
- [ ] Test index deployment process
Publishing
npm run build
npm publishPost-Publishing
- [ ] Test installation:
npm install @merittdev/sdk - [ ] Verify files included in package
- [ ] Test index setup process
- [ ] Update documentation if needed
Integration with Meritt Engine Dashboard
The SDK is designed to work seamlessly with the Meritt Engine Dashboard:
Dashboard Features:
- Visual interface for content management
- Real-time data synchronization
- User authentication and authorization
- File upload and management
- Analytics and reporting
- Settings management
Integration Points:
- SDK provides data layer for dashboard
- Dashboard uses SDK hooks for data fetching
- Dashboard uses SDK types for type safety
- Dashboard uses SDK utilities for formatting and validation
Contact & Support
Repository: https://github.com/merittdev/sdk
Issues: https://github.com/merittdev/sdk/issues
Support: [email protected]
This document should be updated whenever significant architectural decisions are made or implementation details change.
