@sygnl/identity-manager
v1.0.3
Published
Lightweight identity and session management with cookie + localStorage fallback
Maintainers
Readme
@sygnl/identity-manager
Lightweight identity and session management with cookie + localStorage fallback
Features
- 🎯 Zero Dependencies - Fully self-contained
- 🔒 Privacy-First - Anonymous user identification without PII
- 💾 Dual Storage - Cookie + localStorage fallback for maximum persistence
- 🌐 SSR-Safe - Works in both browser and Node.js environments
- ⚙️ Fully Configurable - Customize every aspect of cookie/storage behavior
- 🧪 Well Tested - Comprehensive test suite with >90% coverage
- 📦 TypeScript Native - Full type definitions included
- 🚀 Tiny Bundle - ~2KB minified + gzipped
Installation
npm install @sygnl/identity-manageryarn add @sygnl/identity-managerpnpm add @sygnl/identity-managerQuick Start
import { IdentityManager } from '@sygnl/identity-manager';
// Create instance with default settings
const identity = new IdentityManager();
// Get or create anonymous user ID (365-day persistence)
const userId = identity.ensureAnonymousId();
// → "a3f2b8c9-4d5e-4f6a-8b9c-1d2e3f4a5b6c"
// Get or create session ID (1-day persistence)
const sessionId = identity.ensureSessionId();
// → "sess_1704067200000"Use Cases
Analytics & Tracking
Track user behavior across sessions without requiring login:
const identity = new IdentityManager();
// Track page view with persistent user ID
analytics.track('page_view', {
userId: identity.ensureAnonymousId(),
sessionId: identity.ensureSessionId(),
page: window.location.pathname,
});A/B Testing
Consistently assign users to experiment groups:
const identity = new IdentityManager();
const userId = identity.ensureAnonymousId();
// User always gets same variant across sessions
const variant = hashUserId(userId) % 2 === 0 ? 'A' : 'B';Shopping Cart Persistence
Associate cart with anonymous user before login:
const identity = new IdentityManager();
async function addToCart(productId: string) {
await fetch('/api/cart', {
method: 'POST',
body: JSON.stringify({
anonymousId: identity.ensureAnonymousId(),
productId,
}),
});
}Form Progress Tracking
Resume incomplete forms across sessions:
const identity = new IdentityManager();
const formKey = `form_${identity.ensureAnonymousId()}`;
// Save progress
localStorage.setItem(formKey, JSON.stringify(formData));
// Resume later
const savedData = localStorage.getItem(formKey);API Reference
Constructor
new IdentityManager(options?: PartialIdentityManagerOptions)Creates a new IdentityManager instance with optional configuration.
Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| anonymousIdCookieName | string | '_stid' | Cookie name for anonymous ID |
| sessionIdCookieName | string | '_session_id' | Cookie name for session ID |
| anonymousIdTTL | number | 365 | Anonymous ID TTL in days |
| sessionIdTTL | number | 1 | Session ID TTL in days |
| cookieDomain | string? | undefined | Cookie domain (e.g., .example.com) |
| cookiePath | string | '/' | Cookie path |
| cookieSameSite | 'Strict' \| 'Lax' \| 'None' | 'Lax' | Cookie SameSite attribute |
| cookieSecure | boolean | true | Cookie Secure flag (requires HTTPS) |
| useLocalStorage | boolean | true | Enable localStorage fallback |
| debug | boolean | false | Enable debug logging |
Methods
ensureAnonymousId(): string
Gets existing anonymous ID or creates a new one. Always returns a value.
const userId = identity.ensureAnonymousId();
// → "a3f2b8c9-4d5e-4f6a-8b9c-1d2e3f4a5b6c"Persistence Strategy:
- Check cookie
- Check localStorage (if enabled)
- Generate new UUID v4
- Save to both cookie and localStorage
getAnonymousId(): string | null
Gets existing anonymous ID without creating a new one.
const userId = identity.getAnonymousId();
// → "a3f2b8c9-..." or nullensureSessionId(): string
Gets existing session ID or creates a new one. Always returns a value.
const sessionId = identity.ensureSessionId();
// → "sess_1704067200000"Format: sess_{timestamp}
getSessionId(): string | null
Gets existing session ID without creating a new one.
const sessionId = identity.getSessionId();
// → "sess_1704067200000" or nullgetCookie(name: string): string | null
Low-level cookie getter.
const value = identity.getCookie('my_cookie');setCookie(name: string, value: string, days: number): void
Low-level cookie setter.
identity.setCookie('my_cookie', 'my_value', 30);configure(options: PartialIdentityManagerOptions): void
Updates configuration at runtime.
identity.configure({
debug: true,
anonymousIdTTL: 180,
});Advanced Usage
Custom Cookie Names
Use custom cookie names to avoid conflicts:
const identity = new IdentityManager({
anonymousIdCookieName: '_my_user_id',
sessionIdCookieName: '_my_session',
});Subdomain Sharing
Share identity across subdomains:
const identity = new IdentityManager({
cookieDomain: '.example.com', // Shares across *.example.com
});Extended Anonymous ID Persistence
Keep anonymous ID for longer:
const identity = new IdentityManager({
anonymousIdTTL: 730, // 2 years
});Longer Sessions
Extend session duration:
const identity = new IdentityManager({
sessionIdTTL: 7, // 1 week
});Debug Mode
Enable logging for troubleshooting:
const identity = new IdentityManager({
debug: true,
});
// Logs to console:
// [IdentityManager] ensureAnonymousId: generated new ID { anonymousId: "..." }
// [IdentityManager] setCookie: success { name: "_stid", value: "...", ... }Disable localStorage Fallback
Use cookies only:
const identity = new IdentityManager({
useLocalStorage: false,
});SameSite Configuration
For cross-site tracking (requires Secure):
const identity = new IdentityManager({
cookieSameSite: 'None',
cookieSecure: true, // Required for SameSite=None
});SSR/Node.js Usage
The library is SSR-safe and won't throw in Node.js:
// Server-side rendering
const identity = new IdentityManager();
const userId = identity.ensureAnonymousId(); // Returns null in SSREdge Cases Handled
✅ Cookie Blocking
When cookies are blocked by browser settings:
- Falls back to localStorage
- Gracefully returns null if both fail
- No errors thrown
✅ Private Browsing Mode
When localStorage throws errors:
- Catches and handles silently
- Falls back to cookies only
- Continues working
✅ Storage Quota Exceeded
When localStorage is full:
- Catches quota errors
- Uses cookie as primary storage
- No functionality loss
✅ SSR/Node.js Environment
When document/window are undefined:
- Detects environment
- Returns null gracefully
- No errors thrown
✅ Cookie Expiration
When cookies expire but localStorage persists:
- Automatically restores from localStorage
- Syncs back to cookie
- Seamless recovery
Migration Guide
From Custom Implementation
Before:
function getAnonymousId() {
let id = getCookie('_user_id');
if (!id) {
id = generateUUID();
setCookie('_user_id', id, 365);
}
return id;
}After:
import { IdentityManager } from '@sygnl/identity-manager';
const identity = new IdentityManager({
anonymousIdCookieName: '_user_id', // Match your existing cookie name
});
const id = identity.ensureAnonymousId();From pixel.source.js (Sygnl)
This package extracts and enhances these functions from pixel.source.js:
getCookie()→identity.getCookie()setCookie()→identity.setCookie()generateUUID()→ Internal (automatic)ensureAnonymousId()→identity.ensureAnonymousId()ensureSessionId()→identity.ensureSessionId()
Benefits of migration:
- ✅ TypeScript support
- ✅ Better error handling
- ✅ Configurable options
- ✅ Comprehensive tests
- ✅ SSR safety
- ✅ Maintained package
Browser Compatibility
- ✅ Chrome/Edge 90+
- ✅ Firefox 88+
- ✅ Safari 14+
- ✅ Node.js 18+ (SSR-safe)
UUID Generation:
- Uses
crypto.randomUUID()in modern browsers - Falls back to
Math.random()for older browsers - Both implementations are RFC4122 compliant
Performance
- Bundle Size: ~2KB minified + gzipped
- Runtime: <1ms for ID generation
- Memory: <1KB per instance
- Zero dependencies
Testing
Run the test suite:
npm testRun with coverage:
npm run test:coverageWatch mode for development:
npm run test:watchTest Coverage:
- 60+ test cases
90% code coverage
- Edge cases covered
- Integration tests included
TypeScript
Full TypeScript support included:
import {
IdentityManager,
IdentityManagerOptions,
PartialIdentityManagerOptions,
DEFAULT_OPTIONS
} from '@sygnl/identity-manager';
// Type-safe configuration
const options: PartialIdentityManagerOptions = {
debug: true,
anonymousIdTTL: 365,
};
const identity = new IdentityManager(options);Contributing
Contributions welcome! Please read our Contributing Guide first.
License
Apache-2.0
Copyright 2026 Edge Foundry, Inc.
Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
Related Packages
- @sygnl/event-transport - Analytics event transport layer
- @sygnl/page-engagement - Page engagement tracking
- @sygnl/event-schema - Event schema builder
Made with ❤️ by Sygnl
