Complete TypeScript type reference for the core achievement system.
AchievementDef
type AchievementDef<TId extends string> = {
id: TId;
label: string;
description: string;
hidden?: boolean;
hint?: boolean;
maxProgress?: number;
}
Defines a single achievement. Used in the definitions array passed to createAchievements().
Properties
id
TId extends string
required
Unique identifier for the achievement. Use kebab-case for consistency (e.g., "first-login", "complete-profile").
Display name for the achievement. Shown in UI when unlocked (or always if not hidden).
Longer description explaining how to unlock the achievement.
If true, the id, label, and description are hidden until unlocked. Use for secret achievements.
If true, only the description is hidden until unlocked. The label is still shown. Use for spoiler-free hints.
When provided, this achievement uses progress tracking. It auto-unlocks when progress reaches this value.
Example
const achievement: AchievementDef<'first-login'> = {
id: 'first-login',
label: 'Welcome!',
description: 'Log in for the first time',
};
const progressAchievement: AchievementDef<'complete-profile'> = {
id: 'complete-profile',
label: 'Profile Complete',
description: 'Fill out all 5 profile fields',
maxProgress: 5,
};
const secretAchievement: AchievementDef<'easter-egg'> = {
id: 'easter-egg',
label: 'Secret Discovered',
description: 'Find the hidden easter egg',
hidden: true,
};
AchievementState
type AchievementState<TId extends string> = {
unlockedIds: ReadonlySet<TId>;
progress: Readonly<Record<string, number>>;
toastQueue: ReadonlyArray<TId>;
}
Snapshot of the engine state, passed to subscribers and returned by getState().
Properties
Set of all unlocked achievement IDs. Check membership with state.unlockedIds.has(id).
progress
Readonly<Record<string, number>>
required
Progress values for achievements with maxProgress. Only includes achievements that have been progressed. Access with state.progress[id] ?? 0.
toastQueue
ReadonlyArray<TId>
required
IDs waiting to be shown as notifications, ordered oldest-first. Not persisted (resets on page reload).
Example
const state = engine.getState();
// Check unlocked status
if (state.unlockedIds.has('first-login')) {
console.log('User has logged in before');
}
// Show progress
const progress = state.progress['complete-profile'] ?? 0;
console.log(`Profile: ${progress}/5 fields completed`);
// Display toasts
state.toastQueue.forEach((id) => {
const def = engine.getDefinition(id);
showNotification(def);
engine.dismissToast(id);
});
AchievementEngine
type AchievementEngine<TId extends string> = {
// --- Writes ---
unlock(id: TId): void;
setProgress(id: TId, value: number): void;
incrementProgress(id: TId): void;
collectItem(id: TId, item: string): void;
setMaxProgress(id: TId, max: number): void;
dismissToast(id: TId): void;
reset(): void;
// --- Reads ---
isUnlocked(id: TId): boolean;
getProgress(id: TId): number;
getItems(id: TId): ReadonlySet<string>;
getUnlocked(): ReadonlySet<TId>;
getUnlockedCount(): number;
getState(): AchievementState<TId>;
getDefinition(id: TId): AchievementDef<TId> | undefined;
// --- Reactivity ---
subscribe(listener: (state: AchievementState<TId>) => void): () => void;
}
The engine interface returned by createAchievements(). See AchievementEngine API for detailed method documentation.
Type Parameter
Union type of all achievement IDs. Inferred from the definitions array when using defineAchievements().
Example
const definitions = defineAchievements([
{ id: 'first-login', label: 'Welcome', description: '...' },
{ id: 'complete-profile', label: 'Profile', description: '...' },
]);
type AchievementId = typeof definitions[number]['id'];
// Result: 'first-login' | 'complete-profile'
const engine: AchievementEngine<AchievementId> = createAchievements({
definitions,
});
engine.unlock('first-login'); // ✅ Type-safe
engine.unlock('invalid-id'); // ❌ Type error
StorageAdapter
type StorageAdapter = {
get(key: string): string | null;
set(key: string, value: string): void;
remove(key: string): void;
}
Pluggable storage backend interface. See Storage Adapters for implementations and examples.
Methods
get
(key: string) => string | null
required
Retrieve a value by key. Returns null if not found.
set
(key: string, value: string) => void
required
Store a value with the given key. Overwrites existing value.
remove
(key: string) => void
required
Delete a value by key. No-op if key doesn’t exist.
Example
import { StorageAdapter } from '@achievements-manager/core';
const customAdapter: StorageAdapter = {
get: (key) => {
// Custom retrieval logic
return localStorage.getItem(key);
},
set: (key, value) => {
// Custom storage logic
localStorage.setItem(key, value);
},
remove: (key) => {
// Custom removal logic
localStorage.removeItem(key);
},
};
HashAdapter
type HashAdapter = {
hash(data: string): string;
}
Pluggable hash function interface for tamper detection. See Hash Adapters for implementations and examples.
Methods
hash
(data: string) => string
required
Compute a hash of the input data. Should return a deterministic string.
Example
import { HashAdapter } from '@achievements-manager/core';
const customHashAdapter: HashAdapter = {
hash: (data) => {
// Custom hash logic (example: simple checksum)
let sum = 0;
for (let i = 0; i < data.length; i++) {
sum += data.charCodeAt(i);
}
return sum.toString(36);
},
};
Config (AchievementsConfig)
type Config<TId extends string> = {
definitions: ReadonlyArray<AchievementDef<TId>>;
storage?: StorageAdapter;
hash?: HashAdapter;
onUnlock?: (id: TId) => void;
onTamperDetected?: (key: string) => void;
}
Configuration object passed to createAchievements(). Also exported as AchievementsConfig.
Properties
definitions
ReadonlyArray<AchievementDef<TId>>
required
Array of achievement definitions. Use defineAchievements() for type inference.
storage
StorageAdapter
default:"localStorageAdapter()"
Pluggable storage backend for persisting achievement state.
hash
HashAdapter
default:"fnv1aHashAdapter()"
Pluggable hash function for tamper detection.
Called synchronously immediately after an achievement is unlocked.
Called when stored data fails its integrity check.
Example
import { Config, createAchievements } from '@achievements-manager/core';
const config: Config<'first-login' | 'complete-profile'> = {
definitions: [
{ id: 'first-login', label: 'Welcome', description: '...' },
{ id: 'complete-profile', label: 'Profile', description: '...' },
],
onUnlock: (id) => console.log(`Unlocked: ${id}`),
onTamperDetected: (key) => console.warn(`Tampered: ${key}`),
};
const engine = createAchievements(config);
Type Inference Examples
Deriving ID Union Type
const definitions = defineAchievements([
{ id: 'first-login', label: 'Welcome', description: '...' },
{ id: 'complete-profile', label: 'Profile', description: '...' },
] as const);
type AchievementId = typeof definitions[number]['id'];
// Result: 'first-login' | 'complete-profile'
Type-Safe Engine
const definitions = defineAchievements([
{ id: 'first-login', label: 'Welcome', description: '...' },
]);
type AchievementId = typeof definitions[number]['id'];
const engine = createAchievements<AchievementId>({
definitions,
});
// All methods are type-safe:
engine.unlock('first-login'); // ✅
engine.unlock('typo'); // ❌ Type error
Generic Helper Function
function createTypedEngine<TId extends string>(
definitions: ReadonlyArray<AchievementDef<TId>>
): AchievementEngine<TId> {
return createAchievements({ definitions });
}
const engine = createTypedEngine([
{ id: 'achievement-1', label: 'First', description: '...' },
{ id: 'achievement-2', label: 'Second', description: '...' },
]);
engine.unlock('achievement-1'); // ✅ Type-safe
See Also