react-achievements 3.2.1 → 3.4.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
@@ -51,6 +51,15 @@ interface AchievementStorage {
51
51
  setUnlockedAchievements(achievements: string[]): void;
52
52
  clear(): void;
53
53
  }
54
+ interface AsyncAchievementStorage {
55
+ getMetrics(): Promise<AchievementMetrics>;
56
+ setMetrics(metrics: AchievementMetrics): Promise<void>;
57
+ getUnlockedAchievements(): Promise<string[]>;
58
+ setUnlockedAchievements(achievements: string[]): Promise<void>;
59
+ clear(): Promise<void>;
60
+ }
61
+ type AnyAchievementStorage = AchievementStorage | AsyncAchievementStorage;
62
+ declare function isAsyncStorage(storage: AnyAchievementStorage): storage is AsyncAchievementStorage;
54
63
  interface AchievementContextValue {
55
64
  updateMetrics: (metrics: AchievementMetrics | ((prev: AchievementMetrics) => AchievementMetrics)) => void;
56
65
  unlockedAchievements: string[];
@@ -84,8 +93,10 @@ interface AchievementProviderProps$1 {
84
93
  onAchievementUnlocked?: (achievement: AchievementDetails) => void;
85
94
  }
86
95
  declare enum StorageType {
87
- Local = "local",
88
- Memory = "memory"
96
+ Local = "local",// Synchronous localStorage
97
+ Memory = "memory",// Synchronous in-memory storage
98
+ IndexedDB = "indexeddb",// Asynchronous IndexedDB storage
99
+ RestAPI = "restapi"
89
100
  }
90
101
 
91
102
  declare class LocalStorage implements AchievementStorage {
@@ -115,6 +126,199 @@ declare class MemoryStorage implements AchievementStorage {
115
126
  clear(): void;
116
127
  }
117
128
 
129
+ /**
130
+ * Base error class for all achievement-related errors
131
+ */
132
+ declare class AchievementError extends Error {
133
+ code: string;
134
+ recoverable: boolean;
135
+ remedy?: string | undefined;
136
+ constructor(message: string, code: string, recoverable: boolean, remedy?: string | undefined);
137
+ }
138
+ /**
139
+ * Error thrown when browser storage quota is exceeded
140
+ */
141
+ declare class StorageQuotaError extends AchievementError {
142
+ bytesNeeded: number;
143
+ constructor(bytesNeeded: number);
144
+ }
145
+ /**
146
+ * Error thrown when imported data fails validation
147
+ */
148
+ declare class ImportValidationError extends AchievementError {
149
+ validationErrors: string[];
150
+ constructor(validationErrors: string[]);
151
+ }
152
+ /**
153
+ * Error thrown when storage operations fail
154
+ */
155
+ declare class StorageError extends AchievementError {
156
+ originalError?: Error | undefined;
157
+ constructor(message: string, originalError?: Error | undefined);
158
+ }
159
+ /**
160
+ * Error thrown when configuration is invalid
161
+ */
162
+ declare class ConfigurationError extends AchievementError {
163
+ constructor(message: string);
164
+ }
165
+ /**
166
+ * Error thrown when network sync operations fail
167
+ */
168
+ declare class SyncError extends AchievementError {
169
+ readonly statusCode?: number;
170
+ readonly timeout?: number;
171
+ constructor(message: string, details?: {
172
+ statusCode?: number;
173
+ timeout?: number;
174
+ });
175
+ }
176
+ /**
177
+ * Type guard to check if an error is an AchievementError
178
+ */
179
+ declare function isAchievementError(error: unknown): error is AchievementError;
180
+ /**
181
+ * Type guard to check if an error is recoverable
182
+ */
183
+ declare function isRecoverableError(error: unknown): boolean;
184
+
185
+ declare class AsyncStorageAdapter implements AchievementStorage {
186
+ private asyncStorage;
187
+ private cache;
188
+ private pendingWrites;
189
+ private onError?;
190
+ constructor(asyncStorage: AsyncAchievementStorage, options?: {
191
+ onError?: (error: AchievementError) => void;
192
+ });
193
+ /**
194
+ * Initialize cache by loading from async storage
195
+ * This happens in the background during construction
196
+ */
197
+ private initializeCache;
198
+ /**
199
+ * Wait for cache to be loaded (used internally)
200
+ * Returns immediately if already loaded, otherwise waits
201
+ */
202
+ private ensureCacheLoaded;
203
+ /**
204
+ * SYNC READ: Returns cached metrics immediately
205
+ * Cache is loaded eagerly during construction
206
+ */
207
+ getMetrics(): AchievementMetrics;
208
+ /**
209
+ * SYNC WRITE: Updates cache immediately, writes to storage in background
210
+ * Uses optimistic updates - assumes write will succeed
211
+ */
212
+ setMetrics(metrics: AchievementMetrics): void;
213
+ /**
214
+ * SYNC READ: Returns cached unlocked achievements immediately
215
+ */
216
+ getUnlockedAchievements(): string[];
217
+ /**
218
+ * SYNC WRITE: Updates cache immediately, writes to storage in background
219
+ */
220
+ setUnlockedAchievements(achievements: string[]): void;
221
+ /**
222
+ * SYNC CLEAR: Clears cache immediately, clears storage in background
223
+ */
224
+ clear(): void;
225
+ /**
226
+ * Wait for all pending writes to complete (useful for testing/cleanup)
227
+ * NOT part of AchievementStorage interface - utility method
228
+ */
229
+ flush(): Promise<void>;
230
+ }
231
+
232
+ declare class IndexedDBStorage implements AsyncAchievementStorage {
233
+ private dbName;
234
+ private storeName;
235
+ private db;
236
+ private initPromise;
237
+ constructor(dbName?: string);
238
+ /**
239
+ * Initialize IndexedDB database and object store
240
+ */
241
+ private initDB;
242
+ /**
243
+ * Generic get operation from IndexedDB
244
+ */
245
+ private get;
246
+ /**
247
+ * Generic set operation to IndexedDB
248
+ */
249
+ private set;
250
+ /**
251
+ * Delete operation from IndexedDB
252
+ */
253
+ private delete;
254
+ getMetrics(): Promise<AchievementMetrics>;
255
+ setMetrics(metrics: AchievementMetrics): Promise<void>;
256
+ getUnlockedAchievements(): Promise<string[]>;
257
+ setUnlockedAchievements(achievements: string[]): Promise<void>;
258
+ clear(): Promise<void>;
259
+ }
260
+
261
+ interface RestApiStorageConfig {
262
+ baseUrl: string;
263
+ userId: string;
264
+ headers?: Record<string, string>;
265
+ timeout?: number;
266
+ }
267
+ declare class RestApiStorage implements AsyncAchievementStorage {
268
+ private config;
269
+ constructor(config: RestApiStorageConfig);
270
+ /**
271
+ * Generic fetch wrapper with timeout and error handling
272
+ */
273
+ private fetchWithTimeout;
274
+ getMetrics(): Promise<AchievementMetrics>;
275
+ setMetrics(metrics: AchievementMetrics): Promise<void>;
276
+ getUnlockedAchievements(): Promise<string[]>;
277
+ setUnlockedAchievements(achievements: string[]): Promise<void>;
278
+ clear(): Promise<void>;
279
+ }
280
+
281
+ interface QueuedOperation {
282
+ id: string;
283
+ type: 'setMetrics' | 'setUnlockedAchievements' | 'clear';
284
+ data?: any;
285
+ timestamp: number;
286
+ }
287
+ declare class OfflineQueueStorage implements AsyncAchievementStorage {
288
+ private innerStorage;
289
+ private queue;
290
+ private isOnline;
291
+ private isSyncing;
292
+ private queueStorageKey;
293
+ constructor(innerStorage: AsyncAchievementStorage);
294
+ private loadQueue;
295
+ private saveQueue;
296
+ private handleOnline;
297
+ private handleOffline;
298
+ private processQueue;
299
+ private queueOperation;
300
+ getMetrics(): Promise<AchievementMetrics>;
301
+ setMetrics(metrics: AchievementMetrics): Promise<void>;
302
+ getUnlockedAchievements(): Promise<string[]>;
303
+ setUnlockedAchievements(achievements: string[]): Promise<void>;
304
+ clear(): Promise<void>;
305
+ /**
306
+ * Manually trigger queue processing (useful for testing)
307
+ */
308
+ sync(): Promise<void>;
309
+ /**
310
+ * Get current queue status (useful for debugging)
311
+ */
312
+ getQueueStatus(): {
313
+ pending: number;
314
+ operations: QueuedOperation[];
315
+ };
316
+ /**
317
+ * Cleanup listeners (call on unmount)
318
+ */
319
+ destroy(): void;
320
+ }
321
+
118
322
  interface BadgesButtonProps {
119
323
  onClick: () => void;
120
324
  position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
@@ -137,6 +341,56 @@ interface ConfettiWrapperProps {
137
341
  }
138
342
  declare const ConfettiWrapper: React$1.FC<ConfettiWrapperProps>;
139
343
 
344
+ /**
345
+ * Options for importing achievement data
346
+ */
347
+ interface ImportOptions {
348
+ /** Strategy for merging imported data with existing data */
349
+ mergeStrategy?: 'replace' | 'merge' | 'preserve';
350
+ /** Whether to validate the imported data */
351
+ validate?: boolean;
352
+ /** Optional config hash to validate against */
353
+ expectedConfigHash?: string;
354
+ }
355
+ /**
356
+ * Result of an import operation
357
+ */
358
+ interface ImportResult {
359
+ success: boolean;
360
+ imported: {
361
+ metrics: number;
362
+ achievements: number;
363
+ };
364
+ errors?: string[];
365
+ warnings?: string[];
366
+ }
367
+ /**
368
+ * Imports achievement data from a JSON string
369
+ *
370
+ * @param jsonString - JSON string containing exported achievement data
371
+ * @param currentMetrics - Current metrics state
372
+ * @param currentUnlocked - Current unlocked achievements
373
+ * @param options - Import options
374
+ * @returns Import result with success status and any errors
375
+ *
376
+ * @example
377
+ * ```typescript
378
+ * const result = importAchievementData(
379
+ * jsonString,
380
+ * currentMetrics,
381
+ * currentUnlocked,
382
+ * { mergeStrategy: 'merge', validate: true }
383
+ * );
384
+ *
385
+ * if (result.success) {
386
+ * console.log(`Imported ${result.imported.achievements} achievements`);
387
+ * } else {
388
+ * console.error('Import failed:', result.errors);
389
+ * }
390
+ * ```
391
+ */
392
+ declare function importAchievementData(jsonString: string, currentMetrics: AchievementMetrics, currentUnlocked: string[], options?: ImportOptions): ImportResult;
393
+
140
394
  interface AchievementContextType {
141
395
  update: (metrics: Record<string, any>) => void;
142
396
  achievements: {
@@ -148,13 +402,17 @@ interface AchievementContextType {
148
402
  metrics: AchievementMetrics;
149
403
  unlocked: string[];
150
404
  };
405
+ exportData: () => string;
406
+ importData: (jsonString: string, options?: ImportOptions) => ImportResult;
151
407
  }
152
408
  declare const AchievementContext: React$1.Context<AchievementContextType | undefined>;
153
409
  interface AchievementProviderProps {
154
410
  achievements: AchievementConfigurationType;
155
- storage?: AchievementStorage | StorageType;
411
+ storage?: AchievementStorage | AsyncAchievementStorage | StorageType;
156
412
  children: React$1.ReactNode;
157
413
  icons?: Record<string, string>;
414
+ onError?: (error: AchievementError) => void;
415
+ restApiConfig?: RestApiStorageConfig;
158
416
  }
159
417
  declare const AchievementProvider: React$1.FC<AchievementProviderProps>;
160
418
 
@@ -205,6 +463,18 @@ declare const useSimpleAchievements: () => {
205
463
  metrics: AchievementMetrics;
206
464
  unlocked: string[];
207
465
  };
466
+ /**
467
+ * Export achievement data to JSON string
468
+ * @returns JSON string containing all achievement data
469
+ */
470
+ exportData: () => string;
471
+ /**
472
+ * Import achievement data from JSON string
473
+ * @param jsonString - JSON string containing exported achievement data
474
+ * @param options - Import options (merge strategy, validation)
475
+ * @returns Import result with success status and any errors
476
+ */
477
+ importData: (jsonString: string, options?: ImportOptions) => ImportResult;
208
478
  };
209
479
 
210
480
  declare const defaultStyles: Required<StylesProps>;
@@ -355,4 +625,38 @@ declare class AchievementBuilder {
355
625
  static combine(achievements: (SimpleAchievementConfig | Achievement)[]): SimpleAchievementConfig;
356
626
  }
357
627
 
358
- export { AchievementBuilder, AchievementCondition, AchievementConfiguration, AchievementConfigurationType, AchievementContext, AchievementContextValue, AchievementDetails, AchievementMetricArrayValue, AchievementMetricValue, AchievementMetrics, AchievementProvider, AchievementProviderProps$1 as AchievementProviderProps, AchievementState, AchievementStorage, AwardDetails, BadgesButton, BadgesModal, ConfettiWrapper, CustomAchievementDetails, InitialAchievementMetrics, LocalStorage, MemoryStorage, SimpleAchievementConfig, SimpleAchievementDetails, StorageType, StylesProps, defaultAchievementIcons, defaultStyles, isSimpleConfig, normalizeAchievements, useAchievements, useSimpleAchievements };
628
+ /**
629
+ * Structure of exported achievement data
630
+ */
631
+ interface ExportedData {
632
+ version: string;
633
+ timestamp: number;
634
+ metrics: AchievementMetrics;
635
+ unlockedAchievements: string[];
636
+ configHash?: string;
637
+ }
638
+ /**
639
+ * Exports achievement data to a JSON string
640
+ *
641
+ * @param metrics - Current achievement metrics
642
+ * @param unlocked - Array of unlocked achievement IDs
643
+ * @param configHash - Optional hash of achievement configuration for validation
644
+ * @returns JSON string containing all achievement data
645
+ *
646
+ * @example
647
+ * ```typescript
648
+ * const json = exportAchievementData(metrics, ['score_100', 'level_5']);
649
+ * // Save json to file or send to server
650
+ * ```
651
+ */
652
+ declare function exportAchievementData(metrics: AchievementMetrics, unlocked: string[], configHash?: string): string;
653
+ /**
654
+ * Creates a simple hash of the achievement configuration
655
+ * Used to validate that imported data matches the current configuration
656
+ *
657
+ * @param config - Achievement configuration object
658
+ * @returns Simple hash string
659
+ */
660
+ declare function createConfigHash(config: any): string;
661
+
662
+ export { AchievementBuilder, AchievementCondition, AchievementConfiguration, AchievementConfigurationType, AchievementContext, AchievementContextValue, AchievementDetails, AchievementError, AchievementMetricArrayValue, AchievementMetricValue, AchievementMetrics, AchievementProvider, AchievementProviderProps$1 as AchievementProviderProps, AchievementState, AchievementStorage, AnyAchievementStorage, AsyncAchievementStorage, AsyncStorageAdapter, AwardDetails, BadgesButton, BadgesModal, ConfettiWrapper, ConfigurationError, CustomAchievementDetails, ExportedData, ImportOptions, ImportResult, ImportValidationError, IndexedDBStorage, InitialAchievementMetrics, LocalStorage, MemoryStorage, OfflineQueueStorage, RestApiStorage, RestApiStorageConfig, SimpleAchievementConfig, SimpleAchievementDetails, StorageError, StorageQuotaError, StorageType, StylesProps, SyncError, createConfigHash, defaultAchievementIcons, defaultStyles, exportAchievementData, importAchievementData, isAchievementError, isAsyncStorage, isRecoverableError, isSimpleConfig, normalizeAchievements, useAchievements, useSimpleAchievements };