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.umd.js CHANGED
@@ -1679,6 +1679,276 @@
1679
1679
  }
1680
1680
  }
1681
1681
 
1682
+ /**
1683
+ * Base class for chainable achievement configuration (Tier 2)
1684
+ */
1685
+ class Achievement {
1686
+ constructor(metric, defaultAward) {
1687
+ this.metric = metric;
1688
+ this.award = defaultAward;
1689
+ }
1690
+ /**
1691
+ * Customize the award details for this achievement
1692
+ * @param award - Custom award details
1693
+ * @returns This achievement for chaining
1694
+ */
1695
+ withAward(award) {
1696
+ this.award = Object.assign(Object.assign({}, this.award), award);
1697
+ return this;
1698
+ }
1699
+ }
1700
+ /**
1701
+ * Threshold-based achievement (score, level, etc.)
1702
+ */
1703
+ class ThresholdAchievement extends Achievement {
1704
+ constructor(metric, threshold, defaultAward) {
1705
+ super(metric, defaultAward);
1706
+ this.threshold = threshold;
1707
+ }
1708
+ toConfig() {
1709
+ return {
1710
+ [this.metric]: {
1711
+ [this.threshold]: {
1712
+ title: this.award.title,
1713
+ description: this.award.description,
1714
+ icon: this.award.icon
1715
+ }
1716
+ }
1717
+ };
1718
+ }
1719
+ }
1720
+ /**
1721
+ * Boolean achievement (tutorial completion, first login, etc.)
1722
+ */
1723
+ class BooleanAchievement extends Achievement {
1724
+ toConfig() {
1725
+ return {
1726
+ [this.metric]: {
1727
+ true: {
1728
+ title: this.award.title,
1729
+ description: this.award.description,
1730
+ icon: this.award.icon
1731
+ }
1732
+ }
1733
+ };
1734
+ }
1735
+ }
1736
+ /**
1737
+ * Value-based achievement (character class, difficulty, etc.)
1738
+ */
1739
+ class ValueAchievement extends Achievement {
1740
+ constructor(metric, value, defaultAward) {
1741
+ super(metric, defaultAward);
1742
+ this.value = value;
1743
+ }
1744
+ toConfig() {
1745
+ return {
1746
+ [this.metric]: {
1747
+ [this.value]: {
1748
+ title: this.award.title,
1749
+ description: this.award.description,
1750
+ icon: this.award.icon
1751
+ }
1752
+ }
1753
+ };
1754
+ }
1755
+ }
1756
+ /**
1757
+ * Complex achievement builder for power users (Tier 3)
1758
+ */
1759
+ class ComplexAchievementBuilder {
1760
+ constructor() {
1761
+ this.metric = '';
1762
+ this.condition = null;
1763
+ this.award = {};
1764
+ }
1765
+ /**
1766
+ * Set the metric this achievement tracks
1767
+ */
1768
+ withMetric(metric) {
1769
+ this.metric = metric;
1770
+ return this;
1771
+ }
1772
+ /**
1773
+ * Set the condition function that determines if achievement is unlocked
1774
+ */
1775
+ withCondition(fn) {
1776
+ this.condition = fn;
1777
+ return this;
1778
+ }
1779
+ /**
1780
+ * Set the award details for this achievement
1781
+ */
1782
+ withAward(award) {
1783
+ this.award = Object.assign(Object.assign({}, this.award), award);
1784
+ return this;
1785
+ }
1786
+ /**
1787
+ * Build the final achievement configuration
1788
+ */
1789
+ build() {
1790
+ if (!this.metric || !this.condition) {
1791
+ throw new Error('Complex achievement requires metric and condition');
1792
+ }
1793
+ return {
1794
+ [this.metric]: {
1795
+ custom: {
1796
+ title: this.award.title || this.metric,
1797
+ description: this.award.description || `Achieve ${this.award.title || this.metric}`,
1798
+ icon: this.award.icon || '💎',
1799
+ condition: this.condition
1800
+ }
1801
+ }
1802
+ };
1803
+ }
1804
+ }
1805
+ /**
1806
+ * Main AchievementBuilder with three-tier API
1807
+ * Tier 1: Simple static methods with smart defaults
1808
+ * Tier 2: Chainable customization
1809
+ * Tier 3: Full builder for complex logic
1810
+ */
1811
+ class AchievementBuilder {
1812
+ // TIER 1: Simple Static Methods (90% of use cases)
1813
+ /**
1814
+ * Create a single score achievement with smart defaults
1815
+ * @param threshold - Score threshold to achieve
1816
+ * @returns Chainable ThresholdAchievement
1817
+ */
1818
+ static createScoreAchievement(threshold) {
1819
+ return new ThresholdAchievement('score', threshold, {
1820
+ title: `Score ${threshold}!`,
1821
+ description: `Score ${threshold} points`,
1822
+ icon: '🏆'
1823
+ });
1824
+ }
1825
+ /**
1826
+ * Create multiple score achievements
1827
+ * @param thresholds - Array of thresholds or [threshold, award] tuples
1828
+ * @returns Complete SimpleAchievementConfig
1829
+ */
1830
+ static createScoreAchievements(thresholds) {
1831
+ const config = { score: {} };
1832
+ thresholds.forEach(item => {
1833
+ if (typeof item === 'number') {
1834
+ // Use default award
1835
+ config.score[item] = {
1836
+ title: `Score ${item}!`,
1837
+ description: `Score ${item} points`,
1838
+ icon: '🏆'
1839
+ };
1840
+ }
1841
+ else {
1842
+ // Custom award
1843
+ const [threshold, award] = item;
1844
+ config.score[threshold] = {
1845
+ title: award.title || `Score ${threshold}!`,
1846
+ description: award.description || `Score ${threshold} points`,
1847
+ icon: award.icon || '🏆'
1848
+ };
1849
+ }
1850
+ });
1851
+ return config;
1852
+ }
1853
+ /**
1854
+ * Create a single level achievement with smart defaults
1855
+ * @param level - Level threshold to achieve
1856
+ * @returns Chainable ThresholdAchievement
1857
+ */
1858
+ static createLevelAchievement(level) {
1859
+ return new ThresholdAchievement('level', level, {
1860
+ title: `Level ${level}!`,
1861
+ description: `Reach level ${level}`,
1862
+ icon: '📈'
1863
+ });
1864
+ }
1865
+ /**
1866
+ * Create multiple level achievements
1867
+ * @param levels - Array of levels or [level, award] tuples
1868
+ * @returns Complete SimpleAchievementConfig
1869
+ */
1870
+ static createLevelAchievements(levels) {
1871
+ const config = { level: {} };
1872
+ levels.forEach(item => {
1873
+ if (typeof item === 'number') {
1874
+ // Use default award
1875
+ config.level[item] = {
1876
+ title: `Level ${item}!`,
1877
+ description: `Reach level ${item}`,
1878
+ icon: '📈'
1879
+ };
1880
+ }
1881
+ else {
1882
+ // Custom award
1883
+ const [level, award] = item;
1884
+ config.level[level] = {
1885
+ title: award.title || `Level ${level}!`,
1886
+ description: award.description || `Reach level ${level}`,
1887
+ icon: award.icon || '📈'
1888
+ };
1889
+ }
1890
+ });
1891
+ return config;
1892
+ }
1893
+ /**
1894
+ * Create a boolean achievement with smart defaults
1895
+ * @param metric - The metric name (e.g., 'completedTutorial')
1896
+ * @returns Chainable BooleanAchievement
1897
+ */
1898
+ static createBooleanAchievement(metric) {
1899
+ // Convert camelCase to Title Case
1900
+ const formattedMetric = metric.replace(/([A-Z])/g, ' $1').toLowerCase();
1901
+ const titleCase = formattedMetric.charAt(0).toUpperCase() + formattedMetric.slice(1);
1902
+ return new BooleanAchievement(metric, {
1903
+ title: `${titleCase}!`,
1904
+ description: `Complete ${formattedMetric}`,
1905
+ icon: '✅'
1906
+ });
1907
+ }
1908
+ /**
1909
+ * Create a value-based achievement with smart defaults
1910
+ * @param metric - The metric name (e.g., 'characterClass')
1911
+ * @param value - The value to match (e.g., 'wizard')
1912
+ * @returns Chainable ValueAchievement
1913
+ */
1914
+ static createValueAchievement(metric, value) {
1915
+ const formattedValue = value.charAt(0).toUpperCase() + value.slice(1);
1916
+ return new ValueAchievement(metric, value, {
1917
+ title: `${formattedValue}!`,
1918
+ description: `Choose ${formattedValue.toLowerCase()} for ${metric}`,
1919
+ icon: '🎯'
1920
+ });
1921
+ }
1922
+ // TIER 3: Full Builder for Complex Logic
1923
+ /**
1924
+ * Create a complex achievement builder for power users
1925
+ * @returns ComplexAchievementBuilder for full control
1926
+ */
1927
+ static create() {
1928
+ return new ComplexAchievementBuilder();
1929
+ }
1930
+ // UTILITY METHODS
1931
+ /**
1932
+ * Combine multiple achievement configurations
1933
+ * @param achievements - Array of SimpleAchievementConfig objects or Achievement instances
1934
+ * @returns Combined SimpleAchievementConfig
1935
+ */
1936
+ static combine(achievements) {
1937
+ const combined = {};
1938
+ achievements.forEach(achievement => {
1939
+ const config = achievement instanceof Achievement ? achievement.toConfig() : achievement;
1940
+ Object.keys(config).forEach(key => {
1941
+ if (!combined[key]) {
1942
+ combined[key] = {};
1943
+ }
1944
+ Object.assign(combined[key], config[key]);
1945
+ });
1946
+ });
1947
+ return combined;
1948
+ }
1949
+ }
1950
+
1951
+ exports.AchievementBuilder = AchievementBuilder;
1682
1952
  exports.AchievementEngine = AchievementEngine;
1683
1953
  exports.AchievementError = AchievementError;
1684
1954
  exports.AsyncStorageAdapter = AsyncStorageAdapter;
@@ -1699,6 +1969,7 @@
1699
1969
  exports.isAchievementError = isAchievementError;
1700
1970
  exports.isAsyncStorage = isAsyncStorage;
1701
1971
  exports.isRecoverableError = isRecoverableError;
1972
+ exports.isSimpleConfig = isSimpleConfig;
1702
1973
  exports.normalizeAchievements = normalizeAchievements;
1703
1974
 
1704
1975
  }));