@gamifyio/core 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.
@@ -0,0 +1,778 @@
1
+ /**
2
+ * Configuration options for the Gamify SDK
3
+ */
4
+ interface GamifyConfig {
5
+ /** API key for authentication */
6
+ apiKey: string;
7
+ /** Base URL for the API endpoint */
8
+ endpoint?: string;
9
+ /** Enable debug logging */
10
+ debug?: boolean;
11
+ /** Flush interval in milliseconds (default: 10000) */
12
+ flushInterval?: number;
13
+ /** Maximum events to batch before auto-flush (default: 10) */
14
+ maxBatchSize?: number;
15
+ /** Storage key prefix (default: 'gamify_') */
16
+ storagePrefix?: string;
17
+ }
18
+ /**
19
+ * Event payload structure
20
+ */
21
+ interface GamifyEvent {
22
+ /** Event type (e.g., 'page_view', 'click', 'purchase') */
23
+ type: string;
24
+ /** Event properties */
25
+ properties?: Record<string, unknown>;
26
+ /** Timestamp when event occurred */
27
+ timestamp: string;
28
+ /** User ID if identified */
29
+ userId?: string;
30
+ /** Anonymous ID for unidentified users */
31
+ anonymousId: string;
32
+ }
33
+ /**
34
+ * User traits for identify calls
35
+ */
36
+ interface UserTraits {
37
+ email?: string;
38
+ name?: string;
39
+ [key: string]: unknown;
40
+ }
41
+ /**
42
+ * Internal queued event with metadata
43
+ */
44
+ interface QueuedEvent {
45
+ id: string;
46
+ event: GamifyEvent;
47
+ attempts: number;
48
+ createdAt: number;
49
+ }
50
+ /**
51
+ * API response structure
52
+ */
53
+ interface ApiResponse {
54
+ success: boolean;
55
+ error?: string;
56
+ }
57
+ /**
58
+ * Storage interface for persistence layer
59
+ */
60
+ interface StorageAdapter {
61
+ get<T>(key: string): T | null;
62
+ set<T>(key: string, value: T): void;
63
+ remove(key: string): void;
64
+ clear(): void;
65
+ }
66
+ /**
67
+ * Cart item structure for sessions
68
+ */
69
+ interface CartItem {
70
+ sku: string;
71
+ name: string;
72
+ quantity: number;
73
+ unitPrice: number;
74
+ category?: string;
75
+ brand?: string;
76
+ metadata?: Record<string, unknown>;
77
+ }
78
+ /**
79
+ * Session request to create/update cart
80
+ */
81
+ interface SessionRequest {
82
+ userId: string;
83
+ items: CartItem[];
84
+ coupons?: string[];
85
+ currency?: string;
86
+ }
87
+ /**
88
+ * Applied effect from rule engine
89
+ */
90
+ interface AppliedEffect {
91
+ type: string;
92
+ ruleId?: string;
93
+ params: Record<string, unknown>;
94
+ discountAmount?: number;
95
+ }
96
+ /**
97
+ * Session response from API
98
+ */
99
+ interface SessionResponse {
100
+ sessionToken: string;
101
+ items: CartItem[];
102
+ subtotal: number;
103
+ discount: number;
104
+ total: number;
105
+ coupons: string[];
106
+ appliedEffects: AppliedEffect[];
107
+ rejectedCoupons?: string[];
108
+ currency: string;
109
+ status: string;
110
+ }
111
+ /**
112
+ * Loyalty tier information
113
+ */
114
+ interface LoyaltyTier {
115
+ id: string;
116
+ name: string;
117
+ level: number;
118
+ benefits: Record<string, unknown>;
119
+ color?: string | null;
120
+ iconUrl?: string | null;
121
+ }
122
+ /**
123
+ * Next tier information
124
+ */
125
+ interface NextTierInfo {
126
+ id: string;
127
+ name: string;
128
+ minPoints: number;
129
+ pointsNeeded: number;
130
+ }
131
+ /**
132
+ * Loyalty summary statistics
133
+ */
134
+ interface LoyaltySummary {
135
+ totalEarned: number;
136
+ totalRedeemed: number;
137
+ transactionCount: number;
138
+ }
139
+ /**
140
+ * Customer loyalty profile
141
+ */
142
+ interface LoyaltyProfile {
143
+ userId: string;
144
+ points: number;
145
+ tier: LoyaltyTier | null;
146
+ nextTier: NextTierInfo | null;
147
+ summary: LoyaltySummary;
148
+ }
149
+ /**
150
+ * Loyalty transaction entry
151
+ */
152
+ interface LoyaltyTransaction {
153
+ id: string;
154
+ amount: number;
155
+ balance: number;
156
+ type: 'earn' | 'redeem' | 'expire' | 'adjust' | 'bonus';
157
+ referenceId?: string;
158
+ referenceType?: string;
159
+ description?: string;
160
+ createdAt: string;
161
+ }
162
+ /**
163
+ * Loyalty history response
164
+ */
165
+ interface LoyaltyHistoryResponse {
166
+ transactions: LoyaltyTransaction[];
167
+ total: number;
168
+ }
169
+ /**
170
+ * Affiliate tier/plan information
171
+ */
172
+ interface AffiliateTier {
173
+ id: string;
174
+ name: string;
175
+ type: 'PERCENTAGE' | 'FIXED';
176
+ value: number;
177
+ }
178
+ /**
179
+ * Referral information
180
+ */
181
+ interface ReferralInfo {
182
+ id: string;
183
+ referredExternalId: string;
184
+ referralCode: string;
185
+ createdAt: string;
186
+ }
187
+ /**
188
+ * Affiliate earnings summary
189
+ */
190
+ interface AffiliateEarnings {
191
+ totalEarned: number;
192
+ totalPending: number;
193
+ totalPaid: number;
194
+ transactionCount: number;
195
+ currency: string;
196
+ }
197
+ /**
198
+ * Affiliate statistics profile
199
+ */
200
+ interface AffiliateStats {
201
+ userId: string;
202
+ referralCode: string | null;
203
+ referralCount: number;
204
+ earnings: AffiliateEarnings;
205
+ tier: AffiliateTier | null;
206
+ }
207
+ /**
208
+ * Affiliate stats API response
209
+ */
210
+ interface AffiliateStatsResponse {
211
+ stats: AffiliateStats;
212
+ }
213
+ /**
214
+ * Leaderboard entry
215
+ */
216
+ interface LeaderboardEntry {
217
+ rank: number;
218
+ userId: string;
219
+ displayName?: string;
220
+ referralCount: number;
221
+ totalEarnings: number;
222
+ tier?: AffiliateTier | null;
223
+ }
224
+ /**
225
+ * Leaderboard response
226
+ */
227
+ interface LeaderboardResponse {
228
+ entries: LeaderboardEntry[];
229
+ total: number;
230
+ currentUserRank?: number;
231
+ }
232
+ /**
233
+ * Quest step definition
234
+ */
235
+ interface QuestStep {
236
+ id: string;
237
+ name: string;
238
+ description?: string;
239
+ eventName: string;
240
+ requiredCount: number;
241
+ order: number;
242
+ currentCount: number;
243
+ completed: boolean;
244
+ }
245
+ /**
246
+ * Quest with user progress
247
+ */
248
+ interface QuestWithProgress {
249
+ id: string;
250
+ name: string;
251
+ description?: string;
252
+ xpReward: number;
253
+ badgeReward?: string;
254
+ status: 'not_started' | 'in_progress' | 'completed';
255
+ percentComplete: number;
256
+ steps: QuestStep[];
257
+ startedAt?: string;
258
+ completedAt?: string;
259
+ }
260
+ /**
261
+ * Quests response from API
262
+ */
263
+ interface QuestsResponse {
264
+ quests: QuestWithProgress[];
265
+ }
266
+ /**
267
+ * Streak milestone definition
268
+ */
269
+ interface StreakMilestone {
270
+ day: number;
271
+ rewardXp: number;
272
+ badgeId?: string;
273
+ reached: boolean;
274
+ }
275
+ /**
276
+ * User streak with progress
277
+ */
278
+ interface StreakWithProgress {
279
+ id: string;
280
+ name: string;
281
+ description?: string;
282
+ eventType: string;
283
+ frequency: string;
284
+ currentCount: number;
285
+ maxStreak: number;
286
+ status: string;
287
+ lastActivityDate?: string;
288
+ freezeInventory: number;
289
+ freezeUsedToday: boolean;
290
+ nextMilestone?: {
291
+ day: number;
292
+ rewardXp: number;
293
+ badgeId?: string;
294
+ };
295
+ milestones: StreakMilestone[];
296
+ }
297
+ /**
298
+ * Streaks response from API
299
+ */
300
+ interface StreaksResponse {
301
+ streaks: StreakWithProgress[];
302
+ stats: {
303
+ totalActive: number;
304
+ longestCurrent: number;
305
+ longestEver: number;
306
+ };
307
+ }
308
+ /**
309
+ * Freeze response
310
+ */
311
+ interface FreezeResponse {
312
+ success: boolean;
313
+ remainingFreezes: number;
314
+ message: string;
315
+ }
316
+ /**
317
+ * Badge rarity levels
318
+ */
319
+ type BadgeRarity = 'COMMON' | 'RARE' | 'EPIC' | 'LEGENDARY';
320
+ /**
321
+ * Badge with unlock status
322
+ */
323
+ interface BadgeWithStatus {
324
+ id: string;
325
+ name: string;
326
+ description?: string;
327
+ iconUrl?: string;
328
+ imageUrl?: string;
329
+ rarity: BadgeRarity;
330
+ visibility: 'PUBLIC' | 'HIDDEN';
331
+ category?: string;
332
+ isUnlocked: boolean;
333
+ unlockedAt?: string;
334
+ }
335
+ /**
336
+ * Badges response from API
337
+ */
338
+ interface BadgesResponse {
339
+ badges: BadgeWithStatus[];
340
+ stats: {
341
+ total: number;
342
+ unlocked: number;
343
+ byRarity: Record<string, {
344
+ total: number;
345
+ unlocked: number;
346
+ }>;
347
+ };
348
+ }
349
+ /**
350
+ * Reward item from store
351
+ */
352
+ interface RewardItem {
353
+ id: string;
354
+ name: string;
355
+ description?: string;
356
+ pointsCost: number;
357
+ imageUrl?: string;
358
+ category?: string;
359
+ stock?: number;
360
+ requiredBadgeId?: string;
361
+ requiredBadgeName?: string;
362
+ canAfford: boolean;
363
+ hasBadge: boolean;
364
+ isAvailable: boolean;
365
+ }
366
+ /**
367
+ * Rewards store response
368
+ */
369
+ interface RewardsStoreResponse {
370
+ items: RewardItem[];
371
+ userPoints: number;
372
+ }
373
+ /**
374
+ * Redemption result
375
+ */
376
+ interface RedemptionResult {
377
+ success: boolean;
378
+ transactionId?: string;
379
+ message: string;
380
+ remainingPoints?: number;
381
+ }
382
+
383
+ interface HttpResponse<T> {
384
+ success: boolean;
385
+ data?: T;
386
+ error?: string;
387
+ }
388
+ /**
389
+ * HTTP client for sending events to the API
390
+ * Uses fetch with keepalive for reliable delivery during page unload
391
+ */
392
+ declare class HttpClient {
393
+ private readonly endpoint;
394
+ private readonly apiKey;
395
+ private readonly debug;
396
+ constructor(endpoint: string, apiKey: string, debug?: boolean);
397
+ /**
398
+ * Log debug messages
399
+ */
400
+ private log;
401
+ /**
402
+ * Generic GET request
403
+ */
404
+ get<T>(path: string): Promise<HttpResponse<T>>;
405
+ /**
406
+ * Generic POST request
407
+ */
408
+ post<T>(path: string, body: unknown): Promise<HttpResponse<T>>;
409
+ /**
410
+ * Send a batch of events to the API
411
+ */
412
+ sendEvents(events: GamifyEvent[]): Promise<ApiResponse>;
413
+ /**
414
+ * Send a single event using beacon API (for page unload)
415
+ * Falls back to fetch if beacon is not available
416
+ */
417
+ sendBeacon(events: GamifyEvent[]): boolean;
418
+ }
419
+
420
+ /**
421
+ * SessionManager - Manages cart sessions with discount calculations
422
+ */
423
+ declare class SessionManager {
424
+ private readonly client;
425
+ private readonly storage;
426
+ private readonly debug;
427
+ private sessionToken;
428
+ private cachedSession;
429
+ constructor(config: Required<GamifyConfig>, client: HttpClient, storage: StorageAdapter);
430
+ /**
431
+ * Create or update a session with cart items
432
+ */
433
+ updateCart(userId: string, items: CartItem[], coupons?: string[], currency?: string): Promise<SessionResponse>;
434
+ /**
435
+ * Get current session by token
436
+ */
437
+ getSession(): Promise<SessionResponse | null>;
438
+ /**
439
+ * Apply a coupon to the current session
440
+ */
441
+ applyCoupon(code: string): Promise<SessionResponse>;
442
+ /**
443
+ * Complete the session (checkout)
444
+ */
445
+ complete(): Promise<SessionResponse>;
446
+ /**
447
+ * Clear the current session
448
+ */
449
+ clearSession(): void;
450
+ /**
451
+ * Get cached session data
452
+ */
453
+ getCachedSession(): SessionResponse | null;
454
+ /**
455
+ * Get current session token
456
+ */
457
+ getSessionToken(): string | null;
458
+ /**
459
+ * Check if there's an active session
460
+ */
461
+ hasActiveSession(): boolean;
462
+ private log;
463
+ }
464
+
465
+ /**
466
+ * LoyaltyManager - Manages customer loyalty points and tiers
467
+ */
468
+ declare class LoyaltyManager {
469
+ private readonly client;
470
+ private readonly storage;
471
+ private readonly debug;
472
+ private cachedProfile;
473
+ constructor(config: Required<GamifyConfig>, client: HttpClient, storage: StorageAdapter);
474
+ /**
475
+ * Get customer loyalty profile
476
+ */
477
+ getProfile(userId: string): Promise<LoyaltyProfile>;
478
+ /**
479
+ * Get customer transaction history
480
+ */
481
+ getHistory(userId: string, limit?: number, offset?: number): Promise<LoyaltyHistoryResponse>;
482
+ /**
483
+ * Get cached loyalty profile
484
+ */
485
+ getCachedProfile(): LoyaltyProfile | null;
486
+ /**
487
+ * Get current points balance from cache
488
+ */
489
+ getPoints(): number;
490
+ /**
491
+ * Get current tier from cache
492
+ */
493
+ getTier(): LoyaltyProfile['tier'];
494
+ /**
495
+ * Get next tier info from cache
496
+ */
497
+ getNextTier(): LoyaltyProfile['nextTier'];
498
+ /**
499
+ * Clear cached profile
500
+ */
501
+ clearCache(): void;
502
+ /**
503
+ * Refresh profile from server
504
+ */
505
+ refresh(userId: string): Promise<LoyaltyProfile>;
506
+ private log;
507
+ }
508
+
509
+ /**
510
+ * Referral Manager - Handles URL parameter detection and referrer storage
511
+ *
512
+ * Automatically detects `?ref=code` parameter from URLs and stores
513
+ * the referrer code in localStorage for attribution tracking.
514
+ */
515
+ declare class ReferralManager {
516
+ private readonly config;
517
+ private readonly storage;
518
+ private referrerCode;
519
+ constructor(config: Required<GamifyConfig>, storage: StorageAdapter);
520
+ /**
521
+ * Log debug messages
522
+ */
523
+ private log;
524
+ /**
525
+ * Detect referrer code from current URL
526
+ * Looks for `?ref=` parameter
527
+ */
528
+ detectReferrerFromUrl(): string | null;
529
+ /**
530
+ * Manually set the referrer code
531
+ */
532
+ setReferrer(code: string): void;
533
+ /**
534
+ * Get the current referrer code
535
+ */
536
+ getReferrer(): string | null;
537
+ /**
538
+ * Check if a referrer code is present
539
+ */
540
+ hasReferrer(): boolean;
541
+ /**
542
+ * Clear the referrer code
543
+ */
544
+ clearReferrer(): void;
545
+ /**
546
+ * Get referrer data to include in event properties
547
+ */
548
+ getReferrerProperties(): Record<string, unknown>;
549
+ }
550
+
551
+ /**
552
+ * Affiliate Manager - Handles fetching affiliate stats and leaderboard
553
+ */
554
+ declare class AffiliateManager {
555
+ private readonly config;
556
+ private readonly client;
557
+ private readonly storage;
558
+ private cachedStats;
559
+ private lastFetchTime;
560
+ constructor(config: Required<GamifyConfig>, client: HttpClient, storage: StorageAdapter);
561
+ /**
562
+ * Log debug messages
563
+ */
564
+ private log;
565
+ /**
566
+ * Get affiliate stats for the current user
567
+ * @param userId - User ID to fetch stats for
568
+ * @param forceRefresh - Force refresh from API
569
+ */
570
+ getStats(userId: string, forceRefresh?: boolean): Promise<AffiliateStats>;
571
+ /**
572
+ * Get cached affiliate stats (if available)
573
+ */
574
+ getCachedStats(): AffiliateStats | null;
575
+ /**
576
+ * Get leaderboard data
577
+ * @param limit - Number of entries to fetch (default: 10)
578
+ */
579
+ getLeaderboard(limit?: number): Promise<LeaderboardResponse>;
580
+ /**
581
+ * Clear cached affiliate stats
582
+ */
583
+ clearCache(): void;
584
+ }
585
+
586
+ /**
587
+ * Gamify SDK - Framework-agnostic event tracking
588
+ */
589
+ declare class Gamify {
590
+ private readonly config;
591
+ private readonly storage;
592
+ private readonly queue;
593
+ private readonly client;
594
+ private readonly _session;
595
+ private readonly _loyalty;
596
+ private readonly _referral;
597
+ private readonly _affiliate;
598
+ private flushTimer;
599
+ private anonymousId;
600
+ private userId;
601
+ private userTraits;
602
+ private initialized;
603
+ constructor(config: GamifyConfig);
604
+ /**
605
+ * Initialize the SDK
606
+ */
607
+ private initialize;
608
+ /**
609
+ * Log debug messages
610
+ */
611
+ private log;
612
+ /**
613
+ * Start the automatic flush timer
614
+ */
615
+ private startFlushTimer;
616
+ /**
617
+ * Handle page unload - send remaining events via beacon
618
+ */
619
+ private onBeforeUnload;
620
+ /**
621
+ * Identify a user with optional traits
622
+ * User ID persists across page reloads
623
+ */
624
+ identify(userId: string, traits?: UserTraits): void;
625
+ /**
626
+ * Track an event
627
+ */
628
+ track(eventType: string, properties?: Record<string, unknown>): void;
629
+ /**
630
+ * Flush pending events to the server
631
+ */
632
+ flush(): Promise<void>;
633
+ /**
634
+ * Reset the SDK state (logout user)
635
+ */
636
+ reset(): void;
637
+ /**
638
+ * Get current user ID (null if not identified)
639
+ */
640
+ getUserId(): string | null;
641
+ /**
642
+ * Get anonymous ID
643
+ */
644
+ getAnonymousId(): string;
645
+ /**
646
+ * Get pending event count
647
+ */
648
+ getPendingCount(): number;
649
+ /**
650
+ * Shutdown the SDK gracefully
651
+ */
652
+ shutdown(): void;
653
+ /**
654
+ * Get the session manager
655
+ */
656
+ get session(): SessionManager;
657
+ /**
658
+ * Convenience method: Update cart and get discounts
659
+ */
660
+ updateCart(items: CartItem[], coupons?: string[], currency?: string): Promise<SessionResponse>;
661
+ /**
662
+ * Get the loyalty manager
663
+ */
664
+ get loyalty(): LoyaltyManager;
665
+ /**
666
+ * Convenience method: Get loyalty profile for current user
667
+ */
668
+ getLoyaltyProfile(): Promise<LoyaltyProfile>;
669
+ /**
670
+ * Convenience method: Get loyalty history for current user
671
+ */
672
+ getLoyaltyHistory(limit?: number, offset?: number): Promise<LoyaltyHistoryResponse>;
673
+ /**
674
+ * Get the referral manager
675
+ */
676
+ get referral(): ReferralManager;
677
+ /**
678
+ * Convenience method: Get current referrer code
679
+ */
680
+ getReferrer(): string | null;
681
+ /**
682
+ * Convenience method: Set referrer code manually
683
+ */
684
+ setReferrer(code: string): void;
685
+ /**
686
+ * Get the affiliate manager
687
+ */
688
+ get affiliate(): AffiliateManager;
689
+ /**
690
+ * Convenience method: Get affiliate stats for current user
691
+ */
692
+ getAffiliateStats(forceRefresh?: boolean): Promise<AffiliateStats>;
693
+ /**
694
+ * Convenience method: Get leaderboard
695
+ */
696
+ getLeaderboard(limit?: number): Promise<LeaderboardResponse>;
697
+ /**
698
+ * Get user's quest progress
699
+ */
700
+ getQuests(): Promise<QuestsResponse>;
701
+ /**
702
+ * Get user's streaks with progress
703
+ */
704
+ getStreaks(): Promise<StreaksResponse>;
705
+ /**
706
+ * Use a freeze token for a streak
707
+ */
708
+ useStreakFreeze(ruleId: string): Promise<FreezeResponse>;
709
+ /**
710
+ * Get user's badge collection
711
+ */
712
+ getBadges(category?: string): Promise<BadgesResponse>;
713
+ /**
714
+ * Get available badge categories
715
+ */
716
+ getBadgeCategories(): Promise<string[]>;
717
+ /**
718
+ * Get rewards store items
719
+ */
720
+ getRewardsStore(): Promise<RewardsStoreResponse>;
721
+ /**
722
+ * Redeem a reward item
723
+ */
724
+ redeemReward(itemId: string): Promise<RedemptionResult>;
725
+ }
726
+
727
+ /**
728
+ * Create appropriate storage adapter based on environment
729
+ */
730
+ declare function createStorage(prefix: string): StorageAdapter;
731
+
732
+ /**
733
+ * Event queue for reliable event delivery with offline support
734
+ */
735
+ declare class EventQueue {
736
+ private queue;
737
+ private readonly maxSize;
738
+ private readonly storage;
739
+ constructor(storage: StorageAdapter, maxSize?: number);
740
+ /**
741
+ * Load persisted queue from storage
742
+ */
743
+ private loadFromStorage;
744
+ /**
745
+ * Persist queue to storage
746
+ */
747
+ private saveToStorage;
748
+ /**
749
+ * Add an event to the queue
750
+ */
751
+ enqueue(event: GamifyEvent): string;
752
+ /**
753
+ * Get events ready for sending (batch)
754
+ */
755
+ peek(count: number): QueuedEvent[];
756
+ /**
757
+ * Mark events as successfully sent and remove from queue
758
+ */
759
+ acknowledge(ids: string[]): void;
760
+ /**
761
+ * Mark events as failed (increment retry count)
762
+ */
763
+ nack(ids: string[]): void;
764
+ /**
765
+ * Get current queue size
766
+ */
767
+ size(): number;
768
+ /**
769
+ * Check if queue has pending events
770
+ */
771
+ hasPending(): boolean;
772
+ /**
773
+ * Clear all events from queue
774
+ */
775
+ clear(): void;
776
+ }
777
+
778
+ export { type AffiliateEarnings, AffiliateManager, type AffiliateStats, type AffiliateStatsResponse, type AffiliateTier, type ApiResponse, type AppliedEffect, type BadgeRarity, type BadgeWithStatus, type BadgesResponse, type CartItem, EventQueue, type FreezeResponse, Gamify, type GamifyConfig, type GamifyEvent, HttpClient, type LeaderboardEntry, type LeaderboardResponse, type LoyaltyHistoryResponse, LoyaltyManager, type LoyaltyProfile, type LoyaltySummary, type LoyaltyTier, type LoyaltyTransaction, type NextTierInfo, type QuestStep, type QuestWithProgress, type QuestsResponse, type QueuedEvent, type RedemptionResult, type ReferralInfo, ReferralManager, type RewardItem, type RewardsStoreResponse, SessionManager, type SessionRequest, type SessionResponse, type StorageAdapter, type StreakMilestone, type StreakWithProgress, type StreaksResponse, type UserTraits, createStorage };