achievements-engine 1.0.0 → 1.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/dist/index.d.ts CHANGED
@@ -226,6 +226,14 @@ interface ImportResult$1 {
226
226
  mergedMetrics?: AchievementMetrics;
227
227
  mergedUnlocked?: string[];
228
228
  }
229
+ /**
230
+ * Helper interface for cleaner achievement award definitions
231
+ */
232
+ interface AwardDetails {
233
+ title?: string;
234
+ description?: string;
235
+ icon?: string;
236
+ }
229
237
  /**
230
238
  * Public API surface of the AchievementEngine
231
239
  * This type represents the stable, supported API for external consumers
@@ -272,7 +280,7 @@ declare class AchievementEngine extends EventEmitter {
272
280
  * Update metrics and evaluate achievements
273
281
  * @param newMetrics - Metrics to update
274
282
  */
275
- update(newMetrics: Record<string, any>): void;
283
+ update<T extends Record<string, any>>(newMetrics: Partial<T>): void;
276
284
  /**
277
285
  * Evaluate all achievements and unlock any newly met conditions
278
286
  * This is the core evaluation logic extracted from AchievementProvider
@@ -285,7 +293,7 @@ declare class AchievementEngine extends EventEmitter {
285
293
  /**
286
294
  * Get current metrics (readonly to prevent external modification)
287
295
  */
288
- getMetrics(): Readonly<Record<string, any>>;
296
+ getMetrics<T extends Record<string, any>>(): Readonly<Partial<T>>;
289
297
  /**
290
298
  * Get unlocked achievement IDs (readonly)
291
299
  */
@@ -556,8 +564,19 @@ declare class OfflineQueueStorage implements AsyncAchievementStorage {
556
564
  destroy(): void;
557
565
  }
558
566
 
567
+ declare function isSimpleConfig(config: AchievementConfigurationType): config is SimpleAchievementConfig;
559
568
  declare function normalizeAchievements(config: AchievementConfigurationType): AchievementConfiguration;
560
569
 
570
+ /**
571
+ * Structure of exported achievement data
572
+ */
573
+ interface ExportedData {
574
+ version: string;
575
+ timestamp: number;
576
+ metrics: AchievementMetrics;
577
+ unlockedAchievements: string[];
578
+ configHash?: string;
579
+ }
561
580
  /**
562
581
  * Exports achievement data to a JSON string
563
582
  *
@@ -632,4 +651,125 @@ interface ImportResult {
632
651
  */
633
652
  declare function importAchievementData(jsonString: string, currentMetrics: AchievementMetrics, currentUnlocked: string[], options?: ImportOptions): ImportResult;
634
653
 
635
- export { AchievementCondition, AchievementConfiguration, AchievementConfigurationType, AchievementDetails, AchievementEngine, AchievementEngineApi, AchievementError, AchievementMetricArrayValue, AchievementMetricValue, AchievementMetrics, AchievementState, AchievementStorage, AchievementUnlockedEvent, AchievementWithStatus, AnyAchievementStorage, AsyncAchievementStorage, AsyncStorageAdapter, ConfigurationError, CustomAchievementDetails, EngineConfig, EngineEvent, ErrorEvent, EventEmitter, EventHandler, EventMapping, ImportOptions$1 as ImportOptions, ImportResult$1 as ImportResult, ImportValidationError, IndexedDBStorage, InitialAchievementMetrics, LocalStorage, MemoryStorage, MetricUpdatedEvent, MetricUpdater, OfflineQueueStorage, RestApiStorage, RestApiStorageConfig$1 as RestApiStorageConfig, SimpleAchievementConfig, SimpleAchievementDetails, StateChangedEvent, StorageError, StorageQuotaError, StorageType, SyncError, UnsubscribeFn, createConfigHash, exportAchievementData, importAchievementData, isAchievementError, isAsyncStorage, isRecoverableError, normalizeAchievements };
654
+ /**
655
+ * Base class for chainable achievement configuration (Tier 2)
656
+ */
657
+ declare abstract class Achievement {
658
+ protected metric: string;
659
+ protected award: AwardDetails;
660
+ constructor(metric: string, defaultAward: AwardDetails);
661
+ /**
662
+ * Customize the award details for this achievement
663
+ * @param award - Custom award details
664
+ * @returns This achievement for chaining
665
+ */
666
+ withAward(award: AwardDetails): Achievement;
667
+ /**
668
+ * Convert this achievement to a SimpleAchievementConfig
669
+ */
670
+ abstract toConfig(): SimpleAchievementConfig;
671
+ }
672
+ /**
673
+ * Threshold-based achievement (score, level, etc.)
674
+ */
675
+ declare class ThresholdAchievement extends Achievement {
676
+ private threshold;
677
+ constructor(metric: string, threshold: number, defaultAward: AwardDetails);
678
+ toConfig(): SimpleAchievementConfig;
679
+ }
680
+ /**
681
+ * Boolean achievement (tutorial completion, first login, etc.)
682
+ */
683
+ declare class BooleanAchievement extends Achievement {
684
+ toConfig(): SimpleAchievementConfig;
685
+ }
686
+ /**
687
+ * Value-based achievement (character class, difficulty, etc.)
688
+ */
689
+ declare class ValueAchievement extends Achievement {
690
+ private value;
691
+ constructor(metric: string, value: string, defaultAward: AwardDetails);
692
+ toConfig(): SimpleAchievementConfig;
693
+ }
694
+ /**
695
+ * Complex achievement builder for power users (Tier 3)
696
+ */
697
+ declare class ComplexAchievementBuilder {
698
+ private metric;
699
+ private condition;
700
+ private award;
701
+ /**
702
+ * Set the metric this achievement tracks
703
+ */
704
+ withMetric(metric: string): ComplexAchievementBuilder;
705
+ /**
706
+ * Set the condition function that determines if achievement is unlocked
707
+ */
708
+ withCondition(fn: (metrics: Record<string, any>) => boolean): ComplexAchievementBuilder;
709
+ /**
710
+ * Set the award details for this achievement
711
+ */
712
+ withAward(award: AwardDetails): ComplexAchievementBuilder;
713
+ /**
714
+ * Build the final achievement configuration
715
+ */
716
+ build(): SimpleAchievementConfig;
717
+ }
718
+ /**
719
+ * Main AchievementBuilder with three-tier API
720
+ * Tier 1: Simple static methods with smart defaults
721
+ * Tier 2: Chainable customization
722
+ * Tier 3: Full builder for complex logic
723
+ */
724
+ declare class AchievementBuilder {
725
+ /**
726
+ * Create a single score achievement with smart defaults
727
+ * @param threshold - Score threshold to achieve
728
+ * @returns Chainable ThresholdAchievement
729
+ */
730
+ static createScoreAchievement(threshold: number): ThresholdAchievement;
731
+ /**
732
+ * Create multiple score achievements
733
+ * @param thresholds - Array of thresholds or [threshold, award] tuples
734
+ * @returns Complete SimpleAchievementConfig
735
+ */
736
+ static createScoreAchievements(thresholds: (number | [number, AwardDetails])[]): SimpleAchievementConfig;
737
+ /**
738
+ * Create a single level achievement with smart defaults
739
+ * @param level - Level threshold to achieve
740
+ * @returns Chainable ThresholdAchievement
741
+ */
742
+ static createLevelAchievement(level: number): ThresholdAchievement;
743
+ /**
744
+ * Create multiple level achievements
745
+ * @param levels - Array of levels or [level, award] tuples
746
+ * @returns Complete SimpleAchievementConfig
747
+ */
748
+ static createLevelAchievements(levels: (number | [number, AwardDetails])[]): SimpleAchievementConfig;
749
+ /**
750
+ * Create a boolean achievement with smart defaults
751
+ * @param metric - The metric name (e.g., 'completedTutorial')
752
+ * @returns Chainable BooleanAchievement
753
+ */
754
+ static createBooleanAchievement(metric: string): BooleanAchievement;
755
+ /**
756
+ * Create a value-based achievement with smart defaults
757
+ * @param metric - The metric name (e.g., 'characterClass')
758
+ * @param value - The value to match (e.g., 'wizard')
759
+ * @returns Chainable ValueAchievement
760
+ */
761
+ static createValueAchievement(metric: string, value: string): ValueAchievement;
762
+ /**
763
+ * Create a complex achievement builder for power users
764
+ * @returns ComplexAchievementBuilder for full control
765
+ */
766
+ static create(): ComplexAchievementBuilder;
767
+ /**
768
+ * Combine multiple achievement configurations
769
+ * @param achievements - Array of SimpleAchievementConfig objects or Achievement instances
770
+ * @returns Combined SimpleAchievementConfig
771
+ */
772
+ static combine(achievements: (SimpleAchievementConfig | Achievement)[]): SimpleAchievementConfig;
773
+ }
774
+
775
+ export { AchievementBuilder, AchievementCondition, AchievementConfiguration, AchievementConfigurationType, AchievementDetails, AchievementEngine, AchievementEngineApi, AchievementError, AchievementMetricArrayValue, AchievementMetricValue, AchievementMetrics, AchievementState, AchievementStorage, AchievementUnlockedEvent, AchievementWithStatus, AnyAchievementStorage, AsyncAchievementStorage, AsyncStorageAdapter, AwardDetails, ConfigurationError, CustomAchievementDetails, EngineConfig, EngineEvent, ErrorEvent, EventEmitter, EventHandler, EventMapping, ExportedData, ImportOptions$1 as ImportOptions, ImportResult$1 as ImportResult, ImportValidationError, IndexedDBStorage, InitialAchievementMetrics, LocalStorage, MemoryStorage, MetricUpdatedEvent, MetricUpdater, OfflineQueueStorage, RestApiStorage, RestApiStorageConfig$1 as RestApiStorageConfig, SimpleAchievementConfig, SimpleAchievementDetails, StateChangedEvent, StorageError, StorageQuotaError, StorageType, SyncError, UnsubscribeFn, createConfigHash, exportAchievementData, importAchievementData, isAchievementError, isAsyncStorage, isRecoverableError, isSimpleConfig, normalizeAchievements };
package/dist/index.esm.js CHANGED
@@ -1673,5 +1673,274 @@ class OfflineQueueStorage {
1673
1673
  }
1674
1674
  }
1675
1675
 
1676
- export { AchievementEngine, AchievementError, AsyncStorageAdapter, ConfigurationError, EventEmitter, ImportValidationError, IndexedDBStorage, LocalStorage, MemoryStorage, OfflineQueueStorage, RestApiStorage, StorageError, StorageQuotaError, StorageType, SyncError, createConfigHash, exportAchievementData, importAchievementData, isAchievementError, isAsyncStorage, isRecoverableError, normalizeAchievements };
1676
+ /**
1677
+ * Base class for chainable achievement configuration (Tier 2)
1678
+ */
1679
+ class Achievement {
1680
+ constructor(metric, defaultAward) {
1681
+ this.metric = metric;
1682
+ this.award = defaultAward;
1683
+ }
1684
+ /**
1685
+ * Customize the award details for this achievement
1686
+ * @param award - Custom award details
1687
+ * @returns This achievement for chaining
1688
+ */
1689
+ withAward(award) {
1690
+ this.award = Object.assign(Object.assign({}, this.award), award);
1691
+ return this;
1692
+ }
1693
+ }
1694
+ /**
1695
+ * Threshold-based achievement (score, level, etc.)
1696
+ */
1697
+ class ThresholdAchievement extends Achievement {
1698
+ constructor(metric, threshold, defaultAward) {
1699
+ super(metric, defaultAward);
1700
+ this.threshold = threshold;
1701
+ }
1702
+ toConfig() {
1703
+ return {
1704
+ [this.metric]: {
1705
+ [this.threshold]: {
1706
+ title: this.award.title,
1707
+ description: this.award.description,
1708
+ icon: this.award.icon
1709
+ }
1710
+ }
1711
+ };
1712
+ }
1713
+ }
1714
+ /**
1715
+ * Boolean achievement (tutorial completion, first login, etc.)
1716
+ */
1717
+ class BooleanAchievement extends Achievement {
1718
+ toConfig() {
1719
+ return {
1720
+ [this.metric]: {
1721
+ true: {
1722
+ title: this.award.title,
1723
+ description: this.award.description,
1724
+ icon: this.award.icon
1725
+ }
1726
+ }
1727
+ };
1728
+ }
1729
+ }
1730
+ /**
1731
+ * Value-based achievement (character class, difficulty, etc.)
1732
+ */
1733
+ class ValueAchievement extends Achievement {
1734
+ constructor(metric, value, defaultAward) {
1735
+ super(metric, defaultAward);
1736
+ this.value = value;
1737
+ }
1738
+ toConfig() {
1739
+ return {
1740
+ [this.metric]: {
1741
+ [this.value]: {
1742
+ title: this.award.title,
1743
+ description: this.award.description,
1744
+ icon: this.award.icon
1745
+ }
1746
+ }
1747
+ };
1748
+ }
1749
+ }
1750
+ /**
1751
+ * Complex achievement builder for power users (Tier 3)
1752
+ */
1753
+ class ComplexAchievementBuilder {
1754
+ constructor() {
1755
+ this.metric = '';
1756
+ this.condition = null;
1757
+ this.award = {};
1758
+ }
1759
+ /**
1760
+ * Set the metric this achievement tracks
1761
+ */
1762
+ withMetric(metric) {
1763
+ this.metric = metric;
1764
+ return this;
1765
+ }
1766
+ /**
1767
+ * Set the condition function that determines if achievement is unlocked
1768
+ */
1769
+ withCondition(fn) {
1770
+ this.condition = fn;
1771
+ return this;
1772
+ }
1773
+ /**
1774
+ * Set the award details for this achievement
1775
+ */
1776
+ withAward(award) {
1777
+ this.award = Object.assign(Object.assign({}, this.award), award);
1778
+ return this;
1779
+ }
1780
+ /**
1781
+ * Build the final achievement configuration
1782
+ */
1783
+ build() {
1784
+ if (!this.metric || !this.condition) {
1785
+ throw new Error('Complex achievement requires metric and condition');
1786
+ }
1787
+ return {
1788
+ [this.metric]: {
1789
+ custom: {
1790
+ title: this.award.title || this.metric,
1791
+ description: this.award.description || `Achieve ${this.award.title || this.metric}`,
1792
+ icon: this.award.icon || '💎',
1793
+ condition: this.condition
1794
+ }
1795
+ }
1796
+ };
1797
+ }
1798
+ }
1799
+ /**
1800
+ * Main AchievementBuilder with three-tier API
1801
+ * Tier 1: Simple static methods with smart defaults
1802
+ * Tier 2: Chainable customization
1803
+ * Tier 3: Full builder for complex logic
1804
+ */
1805
+ class AchievementBuilder {
1806
+ // TIER 1: Simple Static Methods (90% of use cases)
1807
+ /**
1808
+ * Create a single score achievement with smart defaults
1809
+ * @param threshold - Score threshold to achieve
1810
+ * @returns Chainable ThresholdAchievement
1811
+ */
1812
+ static createScoreAchievement(threshold) {
1813
+ return new ThresholdAchievement('score', threshold, {
1814
+ title: `Score ${threshold}!`,
1815
+ description: `Score ${threshold} points`,
1816
+ icon: '🏆'
1817
+ });
1818
+ }
1819
+ /**
1820
+ * Create multiple score achievements
1821
+ * @param thresholds - Array of thresholds or [threshold, award] tuples
1822
+ * @returns Complete SimpleAchievementConfig
1823
+ */
1824
+ static createScoreAchievements(thresholds) {
1825
+ const config = { score: {} };
1826
+ thresholds.forEach(item => {
1827
+ if (typeof item === 'number') {
1828
+ // Use default award
1829
+ config.score[item] = {
1830
+ title: `Score ${item}!`,
1831
+ description: `Score ${item} points`,
1832
+ icon: '🏆'
1833
+ };
1834
+ }
1835
+ else {
1836
+ // Custom award
1837
+ const [threshold, award] = item;
1838
+ config.score[threshold] = {
1839
+ title: award.title || `Score ${threshold}!`,
1840
+ description: award.description || `Score ${threshold} points`,
1841
+ icon: award.icon || '🏆'
1842
+ };
1843
+ }
1844
+ });
1845
+ return config;
1846
+ }
1847
+ /**
1848
+ * Create a single level achievement with smart defaults
1849
+ * @param level - Level threshold to achieve
1850
+ * @returns Chainable ThresholdAchievement
1851
+ */
1852
+ static createLevelAchievement(level) {
1853
+ return new ThresholdAchievement('level', level, {
1854
+ title: `Level ${level}!`,
1855
+ description: `Reach level ${level}`,
1856
+ icon: '📈'
1857
+ });
1858
+ }
1859
+ /**
1860
+ * Create multiple level achievements
1861
+ * @param levels - Array of levels or [level, award] tuples
1862
+ * @returns Complete SimpleAchievementConfig
1863
+ */
1864
+ static createLevelAchievements(levels) {
1865
+ const config = { level: {} };
1866
+ levels.forEach(item => {
1867
+ if (typeof item === 'number') {
1868
+ // Use default award
1869
+ config.level[item] = {
1870
+ title: `Level ${item}!`,
1871
+ description: `Reach level ${item}`,
1872
+ icon: '📈'
1873
+ };
1874
+ }
1875
+ else {
1876
+ // Custom award
1877
+ const [level, award] = item;
1878
+ config.level[level] = {
1879
+ title: award.title || `Level ${level}!`,
1880
+ description: award.description || `Reach level ${level}`,
1881
+ icon: award.icon || '📈'
1882
+ };
1883
+ }
1884
+ });
1885
+ return config;
1886
+ }
1887
+ /**
1888
+ * Create a boolean achievement with smart defaults
1889
+ * @param metric - The metric name (e.g., 'completedTutorial')
1890
+ * @returns Chainable BooleanAchievement
1891
+ */
1892
+ static createBooleanAchievement(metric) {
1893
+ // Convert camelCase to Title Case
1894
+ const formattedMetric = metric.replace(/([A-Z])/g, ' $1').toLowerCase();
1895
+ const titleCase = formattedMetric.charAt(0).toUpperCase() + formattedMetric.slice(1);
1896
+ return new BooleanAchievement(metric, {
1897
+ title: `${titleCase}!`,
1898
+ description: `Complete ${formattedMetric}`,
1899
+ icon: '✅'
1900
+ });
1901
+ }
1902
+ /**
1903
+ * Create a value-based achievement with smart defaults
1904
+ * @param metric - The metric name (e.g., 'characterClass')
1905
+ * @param value - The value to match (e.g., 'wizard')
1906
+ * @returns Chainable ValueAchievement
1907
+ */
1908
+ static createValueAchievement(metric, value) {
1909
+ const formattedValue = value.charAt(0).toUpperCase() + value.slice(1);
1910
+ return new ValueAchievement(metric, value, {
1911
+ title: `${formattedValue}!`,
1912
+ description: `Choose ${formattedValue.toLowerCase()} for ${metric}`,
1913
+ icon: '🎯'
1914
+ });
1915
+ }
1916
+ // TIER 3: Full Builder for Complex Logic
1917
+ /**
1918
+ * Create a complex achievement builder for power users
1919
+ * @returns ComplexAchievementBuilder for full control
1920
+ */
1921
+ static create() {
1922
+ return new ComplexAchievementBuilder();
1923
+ }
1924
+ // UTILITY METHODS
1925
+ /**
1926
+ * Combine multiple achievement configurations
1927
+ * @param achievements - Array of SimpleAchievementConfig objects or Achievement instances
1928
+ * @returns Combined SimpleAchievementConfig
1929
+ */
1930
+ static combine(achievements) {
1931
+ const combined = {};
1932
+ achievements.forEach(achievement => {
1933
+ const config = achievement instanceof Achievement ? achievement.toConfig() : achievement;
1934
+ Object.keys(config).forEach(key => {
1935
+ if (!combined[key]) {
1936
+ combined[key] = {};
1937
+ }
1938
+ Object.assign(combined[key], config[key]);
1939
+ });
1940
+ });
1941
+ return combined;
1942
+ }
1943
+ }
1944
+
1945
+ export { AchievementBuilder, AchievementEngine, AchievementError, AsyncStorageAdapter, ConfigurationError, EventEmitter, ImportValidationError, IndexedDBStorage, LocalStorage, MemoryStorage, OfflineQueueStorage, RestApiStorage, StorageError, StorageQuotaError, StorageType, SyncError, createConfigHash, exportAchievementData, importAchievementData, isAchievementError, isAsyncStorage, isRecoverableError, isSimpleConfig, normalizeAchievements };
1677
1946
  //# sourceMappingURL=index.esm.js.map