achievements-engine 1.0.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/README.md ADDED
@@ -0,0 +1,363 @@
1
+ # achievements-engine
2
+
3
+ A framework-agnostic achievement system with event-based architecture. Perfect for adding gamification features to any JavaScript application.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **Event-Based**: Track achievements using custom events or direct metric updates
8
+ - 🔄 **Framework Agnostic**: Works with React, Vue, Angular, Node.js, or vanilla JavaScript
9
+ - 💾 **Flexible Storage**: Built-in support for Memory, LocalStorage, IndexedDB, and REST API
10
+ - 📦 **Zero Dependencies**: Lightweight core with no external dependencies
11
+ - 🎨 **TypeScript**: Full TypeScript support with comprehensive type definitions
12
+ - 🔌 **Extensible**: Easy to add custom storage backends and achievement conditions
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install achievements-engine
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import { AchievementEngine } from 'achievements-engine';
24
+
25
+ // Define your achievements
26
+ const achievements = {
27
+ score: {
28
+ 100: { title: 'Century!', description: 'Score 100 points', icon: '🏆' },
29
+ 500: { title: 'High Scorer!', description: 'Score 500 points', icon: '⭐' },
30
+ },
31
+ level: {
32
+ 5: { title: 'Level 5', description: 'Reach level 5', icon: '🎮' },
33
+ }
34
+ };
35
+
36
+ // Create the engine
37
+ const engine = new AchievementEngine({
38
+ achievements,
39
+ storage: 'local' // Use browser localStorage
40
+ });
41
+
42
+ // Listen for achievement unlocks
43
+ engine.on('achievement:unlocked', (achievement) => {
44
+ console.log(`Achievement unlocked: ${achievement.achievementTitle}!`);
45
+ });
46
+
47
+ // Update metrics
48
+ engine.update({ score: 100 }); // Unlocks "Century!"
49
+ engine.update({ score: 500 }); // Unlocks "High Scorer!"
50
+ ```
51
+
52
+ ## Event-Based Tracking
53
+
54
+ The engine supports event-based tracking with automatic metric mapping:
55
+
56
+ ```typescript
57
+ const engine = new AchievementEngine({
58
+ achievements,
59
+ // Map events to metrics
60
+ eventMapping: {
61
+ 'levelUp': 'level', // Direct mapping
62
+ 'scoreChanged': 'score', // Event name -> metric name
63
+
64
+ // Custom transformer function
65
+ 'playerAction': (data, currentMetrics) => ({
66
+ score: currentMetrics.score + data.points,
67
+ combo: data.isCombo ? currentMetrics.combo + 1 : 0
68
+ })
69
+ }
70
+ });
71
+
72
+ // Use events instead of direct updates
73
+ engine.emit('levelUp', 5);
74
+ engine.emit('scoreChanged', 250);
75
+ engine.emit('playerAction', { points: 100, isCombo: true });
76
+ ```
77
+
78
+ ## Achievement Configuration
79
+
80
+ ### Simple API (Threshold-Based)
81
+
82
+ ```typescript
83
+ const achievements = {
84
+ // Numeric thresholds
85
+ score: {
86
+ 100: { title: 'Beginner', icon: '🌱' },
87
+ 1000: { title: 'Expert', icon: '🏆' },
88
+ },
89
+
90
+ // Boolean achievements
91
+ completedTutorial: {
92
+ true: { title: 'Tutorial Complete', icon: '📚' }
93
+ },
94
+
95
+ // Custom conditions
96
+ combo: {
97
+ custom: {
98
+ title: 'Perfect Combo',
99
+ description: 'Score 1000+ with 100% accuracy',
100
+ icon: '💎',
101
+ condition: (metrics) => metrics.score >= 1000 && metrics.accuracy === 100
102
+ }
103
+ }
104
+ };
105
+ ```
106
+
107
+ ### Complex API (Advanced)
108
+
109
+ For more control, use the complex format:
110
+
111
+ ```typescript
112
+ const achievements = {
113
+ score: [{
114
+ isConditionMet: (value) => value >= 100,
115
+ achievementDetails: {
116
+ achievementId: 'score_100',
117
+ achievementTitle: 'Century!',
118
+ achievementDescription: 'Score 100 points',
119
+ achievementIconKey: 'trophy'
120
+ }
121
+ }]
122
+ };
123
+ ```
124
+
125
+ ## Storage Options
126
+
127
+ ### Memory Storage (Default)
128
+ ```typescript
129
+ const engine = new AchievementEngine({
130
+ achievements,
131
+ storage: 'memory' // Data lost on page reload
132
+ });
133
+ ```
134
+
135
+ ### Browser LocalStorage
136
+ ```typescript
137
+ const engine = new AchievementEngine({
138
+ achievements,
139
+ storage: 'local' // Persists in browser
140
+ });
141
+ ```
142
+
143
+ ### IndexedDB (Async)
144
+ ```typescript
145
+ const engine = new AchievementEngine({
146
+ achievements,
147
+ storage: 'indexeddb' // For large datasets
148
+ });
149
+ ```
150
+
151
+ ### REST API
152
+ ```typescript
153
+ const engine = new AchievementEngine({
154
+ achievements,
155
+ storage: 'restapi',
156
+ restApiConfig: {
157
+ baseUrl: 'https://api.example.com',
158
+ userId: 'user123',
159
+ headers: {
160
+ 'Authorization': 'Bearer token'
161
+ }
162
+ }
163
+ });
164
+ ```
165
+
166
+ ### Custom Storage
167
+ ```typescript
168
+ import { AchievementStorage } from 'achievements-engine';
169
+
170
+ class CustomStorage implements AchievementStorage {
171
+ getMetrics() { /* ... */ }
172
+ setMetrics(metrics) { /* ... */ }
173
+ getUnlockedAchievements() { /* ... */ }
174
+ setUnlockedAchievements(achievements) { /* ... */ }
175
+ clear() { /* ... */ }
176
+ }
177
+
178
+ const engine = new AchievementEngine({
179
+ achievements,
180
+ storage: new CustomStorage()
181
+ });
182
+ ```
183
+
184
+ ## Events
185
+
186
+ Listen to engine events for real-time updates:
187
+
188
+ ```typescript
189
+ // Achievement unlocked
190
+ engine.on('achievement:unlocked', (event) => {
191
+ console.log(event.achievementTitle);
192
+ console.log(event.achievementDescription);
193
+ console.log(event.achievementIconKey);
194
+ console.log(event.timestamp);
195
+ });
196
+
197
+ // Metric updated
198
+ engine.on('metric:updated', (event) => {
199
+ console.log(`${event.metric}: ${event.oldValue} → ${event.newValue}`);
200
+ });
201
+
202
+ // State changed
203
+ engine.on('state:changed', (event) => {
204
+ console.log('Metrics:', event.metrics);
205
+ console.log('Unlocked:', event.unlocked);
206
+ });
207
+
208
+ // Errors
209
+ engine.on('error', (event) => {
210
+ console.error(event.error);
211
+ console.error('Context:', event.context);
212
+ });
213
+
214
+ // Unsubscribe
215
+ const unsubscribe = engine.on('achievement:unlocked', handler);
216
+ unsubscribe(); // Remove listener
217
+ ```
218
+
219
+ ## State Access
220
+
221
+ ```typescript
222
+ // Get current metrics (readonly)
223
+ const metrics = engine.getMetrics();
224
+ console.log(metrics.score); // 100
225
+
226
+ // Get unlocked achievement IDs (readonly)
227
+ const unlocked = engine.getUnlocked();
228
+ console.log(unlocked); // ['score_100', 'level_5']
229
+
230
+ // Get all achievements with status
231
+ const allAchievements = engine.getAllAchievements();
232
+ allAchievements.forEach(achievement => {
233
+ console.log(achievement.achievementTitle);
234
+ console.log(achievement.isUnlocked); // true or false
235
+ });
236
+ ```
237
+
238
+ ## Import/Export
239
+
240
+ ```typescript
241
+ // Export achievement data
242
+ const data = engine.export(); // Returns JSON string
243
+ localStorage.setItem('savedProgress', data);
244
+
245
+ // Import achievement data
246
+ const savedData = localStorage.getItem('savedProgress');
247
+ const result = engine.import(savedData, {
248
+ merge: true, // Merge with existing data
249
+ validate: true // Validate config matches
250
+ });
251
+
252
+ if (result.success) {
253
+ console.log('Data imported successfully');
254
+ } else {
255
+ console.error('Import errors:', result.errors);
256
+ }
257
+ ```
258
+
259
+ ## Reset
260
+
261
+ ```typescript
262
+ // Clear all achievement data
263
+ engine.reset();
264
+ ```
265
+
266
+ ## Cleanup
267
+
268
+ ```typescript
269
+ // Remove all event listeners
270
+ engine.destroy();
271
+ ```
272
+
273
+ ## Multiple Instances
274
+
275
+ You can create multiple independent engines:
276
+
277
+ ```typescript
278
+ const playerEngine = new AchievementEngine({
279
+ achievements: playerAchievements,
280
+ storage: 'local'
281
+ });
282
+
283
+ const teamEngine = new AchievementEngine({
284
+ achievements: teamAchievements,
285
+ storage: 'local'
286
+ });
287
+ ```
288
+
289
+ ## TypeScript Support
290
+
291
+ Full TypeScript support with type inference:
292
+
293
+ ```typescript
294
+ import {
295
+ AchievementEngine,
296
+ EngineConfig,
297
+ AchievementUnlockedEvent,
298
+ SimpleAchievementConfig
299
+ } from 'achievements-engine';
300
+
301
+ const config: EngineConfig = {
302
+ achievements: {
303
+ score: {
304
+ 100: { title: 'First Score', icon: '🎯' }
305
+ }
306
+ }
307
+ };
308
+
309
+ const engine = new AchievementEngine(config);
310
+
311
+ engine.on('achievement:unlocked', (event: AchievementUnlockedEvent) => {
312
+ console.log(event.achievementTitle);
313
+ });
314
+ ```
315
+
316
+ ## Error Handling
317
+
318
+ ```typescript
319
+ const engine = new AchievementEngine({
320
+ achievements,
321
+ onError: (error) => {
322
+ // Handle async storage errors, etc.
323
+ console.error('Achievement engine error:', error);
324
+ }
325
+ });
326
+
327
+ // Or listen to error events
328
+ engine.on('error', (event) => {
329
+ console.error(event.error);
330
+ });
331
+ ```
332
+
333
+ ## Use Cases
334
+
335
+ - **Games**: Track player progress and unlock achievements
336
+ - **Learning Platforms**: Reward course completion and milestones
337
+ - **Fitness Apps**: Track workout streaks and personal records
338
+ - **Productivity Apps**: Encourage task completion and habits
339
+ - **Social Apps**: Gamify user engagement
340
+
341
+ ## Browser Support
342
+
343
+ - Modern browsers (Chrome, Firefox, Safari, Edge)
344
+ - Node.js 14+
345
+ - Works in Web Workers
346
+
347
+ ## License
348
+
349
+ MIT
350
+
351
+ ## Contributing
352
+
353
+ Contributions welcome! Please open an issue or PR.
354
+
355
+ ## Links
356
+
357
+ - [npm package](https://www.npmjs.com/package/achievements-engine) (to be published)
358
+ - [GitHub repository](https://github.com/dave-b-b/achievements-engine) (to be created)
359
+ - [Test Specification](./TEST_SPECIFICATION.md)
360
+
361
+ ---
362
+
363
+ Built with ❤️ for the gamification community