@sygnl/identity-manager 1.0.1
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/LICENSE +12 -0
- package/README.md +467 -0
- package/dist/index.cjs +252 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +153 -0
- package/dist/index.d.ts +153 -0
- package/dist/index.js +249 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
All Rights Reserved
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Edge Foundry, Inc.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are proprietary
|
|
6
|
+
and confidential to Edge Foundry, Inc.
|
|
7
|
+
|
|
8
|
+
Unauthorized copying, modification, distribution, or use of this Software,
|
|
9
|
+
via any medium, is strictly prohibited without the express written permission
|
|
10
|
+
of Edge Foundry, Inc.
|
|
11
|
+
|
|
12
|
+
For licensing inquiries, please contact: legal@sygnl.com
|
package/README.md
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
# @sygnl/identity-manager
|
|
2
|
+
|
|
3
|
+
> Lightweight identity and session management with cookie + localStorage fallback
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@sygnl/identity-manager)
|
|
6
|
+
[](https://github.com/sygnl/identity-manager/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- ๐ฏ **Zero Dependencies** - Fully self-contained
|
|
11
|
+
- ๐ **Privacy-First** - Anonymous user identification without PII
|
|
12
|
+
- ๐พ **Dual Storage** - Cookie + localStorage fallback for maximum persistence
|
|
13
|
+
- ๐ **SSR-Safe** - Works in both browser and Node.js environments
|
|
14
|
+
- โ๏ธ **Fully Configurable** - Customize every aspect of cookie/storage behavior
|
|
15
|
+
- ๐งช **Well Tested** - Comprehensive test suite with >90% coverage
|
|
16
|
+
- ๐ฆ **TypeScript Native** - Full type definitions included
|
|
17
|
+
- ๐ **Tiny Bundle** - ~2KB minified + gzipped
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @sygnl/identity-manager
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
yarn add @sygnl/identity-manager
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm add @sygnl/identity-manager
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { IdentityManager } from '@sygnl/identity-manager';
|
|
37
|
+
|
|
38
|
+
// Create instance with default settings
|
|
39
|
+
const identity = new IdentityManager();
|
|
40
|
+
|
|
41
|
+
// Get or create anonymous user ID (365-day persistence)
|
|
42
|
+
const userId = identity.ensureAnonymousId();
|
|
43
|
+
// โ "a3f2b8c9-4d5e-4f6a-8b9c-1d2e3f4a5b6c"
|
|
44
|
+
|
|
45
|
+
// Get or create session ID (1-day persistence)
|
|
46
|
+
const sessionId = identity.ensureSessionId();
|
|
47
|
+
// โ "sess_1704067200000"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Use Cases
|
|
51
|
+
|
|
52
|
+
### Analytics & Tracking
|
|
53
|
+
|
|
54
|
+
Track user behavior across sessions without requiring login:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const identity = new IdentityManager();
|
|
58
|
+
|
|
59
|
+
// Track page view with persistent user ID
|
|
60
|
+
analytics.track('page_view', {
|
|
61
|
+
userId: identity.ensureAnonymousId(),
|
|
62
|
+
sessionId: identity.ensureSessionId(),
|
|
63
|
+
page: window.location.pathname,
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### A/B Testing
|
|
68
|
+
|
|
69
|
+
Consistently assign users to experiment groups:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
const identity = new IdentityManager();
|
|
73
|
+
const userId = identity.ensureAnonymousId();
|
|
74
|
+
|
|
75
|
+
// User always gets same variant across sessions
|
|
76
|
+
const variant = hashUserId(userId) % 2 === 0 ? 'A' : 'B';
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Shopping Cart Persistence
|
|
80
|
+
|
|
81
|
+
Associate cart with anonymous user before login:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
const identity = new IdentityManager();
|
|
85
|
+
|
|
86
|
+
async function addToCart(productId: string) {
|
|
87
|
+
await fetch('/api/cart', {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
body: JSON.stringify({
|
|
90
|
+
anonymousId: identity.ensureAnonymousId(),
|
|
91
|
+
productId,
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Form Progress Tracking
|
|
98
|
+
|
|
99
|
+
Resume incomplete forms across sessions:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const identity = new IdentityManager();
|
|
103
|
+
const formKey = `form_${identity.ensureAnonymousId()}`;
|
|
104
|
+
|
|
105
|
+
// Save progress
|
|
106
|
+
localStorage.setItem(formKey, JSON.stringify(formData));
|
|
107
|
+
|
|
108
|
+
// Resume later
|
|
109
|
+
const savedData = localStorage.getItem(formKey);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## API Reference
|
|
113
|
+
|
|
114
|
+
### Constructor
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
new IdentityManager(options?: PartialIdentityManagerOptions)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Creates a new IdentityManager instance with optional configuration.
|
|
121
|
+
|
|
122
|
+
#### Options
|
|
123
|
+
|
|
124
|
+
| Option | Type | Default | Description |
|
|
125
|
+
|--------|------|---------|-------------|
|
|
126
|
+
| `anonymousIdCookieName` | `string` | `'_stid'` | Cookie name for anonymous ID |
|
|
127
|
+
| `sessionIdCookieName` | `string` | `'_session_id'` | Cookie name for session ID |
|
|
128
|
+
| `anonymousIdTTL` | `number` | `365` | Anonymous ID TTL in days |
|
|
129
|
+
| `sessionIdTTL` | `number` | `1` | Session ID TTL in days |
|
|
130
|
+
| `cookieDomain` | `string?` | `undefined` | Cookie domain (e.g., `.example.com`) |
|
|
131
|
+
| `cookiePath` | `string` | `'/'` | Cookie path |
|
|
132
|
+
| `cookieSameSite` | `'Strict' \| 'Lax' \| 'None'` | `'Lax'` | Cookie SameSite attribute |
|
|
133
|
+
| `cookieSecure` | `boolean` | `true` | Cookie Secure flag (requires HTTPS) |
|
|
134
|
+
| `useLocalStorage` | `boolean` | `true` | Enable localStorage fallback |
|
|
135
|
+
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
136
|
+
|
|
137
|
+
### Methods
|
|
138
|
+
|
|
139
|
+
#### `ensureAnonymousId(): string`
|
|
140
|
+
|
|
141
|
+
Gets existing anonymous ID or creates a new one. Always returns a value.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const userId = identity.ensureAnonymousId();
|
|
145
|
+
// โ "a3f2b8c9-4d5e-4f6a-8b9c-1d2e3f4a5b6c"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Persistence Strategy:**
|
|
149
|
+
1. Check cookie
|
|
150
|
+
2. Check localStorage (if enabled)
|
|
151
|
+
3. Generate new UUID v4
|
|
152
|
+
4. Save to both cookie and localStorage
|
|
153
|
+
|
|
154
|
+
#### `getAnonymousId(): string | null`
|
|
155
|
+
|
|
156
|
+
Gets existing anonymous ID without creating a new one.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const userId = identity.getAnonymousId();
|
|
160
|
+
// โ "a3f2b8c9-..." or null
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### `ensureSessionId(): string`
|
|
164
|
+
|
|
165
|
+
Gets existing session ID or creates a new one. Always returns a value.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const sessionId = identity.ensureSessionId();
|
|
169
|
+
// โ "sess_1704067200000"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Format:** `sess_{timestamp}`
|
|
173
|
+
|
|
174
|
+
#### `getSessionId(): string | null`
|
|
175
|
+
|
|
176
|
+
Gets existing session ID without creating a new one.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const sessionId = identity.getSessionId();
|
|
180
|
+
// โ "sess_1704067200000" or null
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### `getCookie(name: string): string | null`
|
|
184
|
+
|
|
185
|
+
Low-level cookie getter.
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const value = identity.getCookie('my_cookie');
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### `setCookie(name: string, value: string, days: number): void`
|
|
192
|
+
|
|
193
|
+
Low-level cookie setter.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
identity.setCookie('my_cookie', 'my_value', 30);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### `configure(options: PartialIdentityManagerOptions): void`
|
|
200
|
+
|
|
201
|
+
Updates configuration at runtime.
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
identity.configure({
|
|
205
|
+
debug: true,
|
|
206
|
+
anonymousIdTTL: 180,
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Advanced Usage
|
|
211
|
+
|
|
212
|
+
### Custom Cookie Names
|
|
213
|
+
|
|
214
|
+
Use custom cookie names to avoid conflicts:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
const identity = new IdentityManager({
|
|
218
|
+
anonymousIdCookieName: '_my_user_id',
|
|
219
|
+
sessionIdCookieName: '_my_session',
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Subdomain Sharing
|
|
224
|
+
|
|
225
|
+
Share identity across subdomains:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const identity = new IdentityManager({
|
|
229
|
+
cookieDomain: '.example.com', // Shares across *.example.com
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Extended Anonymous ID Persistence
|
|
234
|
+
|
|
235
|
+
Keep anonymous ID for longer:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
const identity = new IdentityManager({
|
|
239
|
+
anonymousIdTTL: 730, // 2 years
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Longer Sessions
|
|
244
|
+
|
|
245
|
+
Extend session duration:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
const identity = new IdentityManager({
|
|
249
|
+
sessionIdTTL: 7, // 1 week
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Debug Mode
|
|
254
|
+
|
|
255
|
+
Enable logging for troubleshooting:
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
const identity = new IdentityManager({
|
|
259
|
+
debug: true,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Logs to console:
|
|
263
|
+
// [IdentityManager] ensureAnonymousId: generated new ID { anonymousId: "..." }
|
|
264
|
+
// [IdentityManager] setCookie: success { name: "_stid", value: "...", ... }
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Disable localStorage Fallback
|
|
268
|
+
|
|
269
|
+
Use cookies only:
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
const identity = new IdentityManager({
|
|
273
|
+
useLocalStorage: false,
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### SameSite Configuration
|
|
278
|
+
|
|
279
|
+
For cross-site tracking (requires Secure):
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
const identity = new IdentityManager({
|
|
283
|
+
cookieSameSite: 'None',
|
|
284
|
+
cookieSecure: true, // Required for SameSite=None
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### SSR/Node.js Usage
|
|
289
|
+
|
|
290
|
+
The library is SSR-safe and won't throw in Node.js:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
// Server-side rendering
|
|
294
|
+
const identity = new IdentityManager();
|
|
295
|
+
const userId = identity.ensureAnonymousId(); // Returns null in SSR
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Edge Cases Handled
|
|
299
|
+
|
|
300
|
+
### โ
Cookie Blocking
|
|
301
|
+
|
|
302
|
+
When cookies are blocked by browser settings:
|
|
303
|
+
- Falls back to localStorage
|
|
304
|
+
- Gracefully returns null if both fail
|
|
305
|
+
- No errors thrown
|
|
306
|
+
|
|
307
|
+
### โ
Private Browsing Mode
|
|
308
|
+
|
|
309
|
+
When localStorage throws errors:
|
|
310
|
+
- Catches and handles silently
|
|
311
|
+
- Falls back to cookies only
|
|
312
|
+
- Continues working
|
|
313
|
+
|
|
314
|
+
### โ
Storage Quota Exceeded
|
|
315
|
+
|
|
316
|
+
When localStorage is full:
|
|
317
|
+
- Catches quota errors
|
|
318
|
+
- Uses cookie as primary storage
|
|
319
|
+
- No functionality loss
|
|
320
|
+
|
|
321
|
+
### โ
SSR/Node.js Environment
|
|
322
|
+
|
|
323
|
+
When `document`/`window` are undefined:
|
|
324
|
+
- Detects environment
|
|
325
|
+
- Returns null gracefully
|
|
326
|
+
- No errors thrown
|
|
327
|
+
|
|
328
|
+
### โ
Cookie Expiration
|
|
329
|
+
|
|
330
|
+
When cookies expire but localStorage persists:
|
|
331
|
+
- Automatically restores from localStorage
|
|
332
|
+
- Syncs back to cookie
|
|
333
|
+
- Seamless recovery
|
|
334
|
+
|
|
335
|
+
## Migration Guide
|
|
336
|
+
|
|
337
|
+
### From Custom Implementation
|
|
338
|
+
|
|
339
|
+
**Before:**
|
|
340
|
+
```javascript
|
|
341
|
+
function getAnonymousId() {
|
|
342
|
+
let id = getCookie('_user_id');
|
|
343
|
+
if (!id) {
|
|
344
|
+
id = generateUUID();
|
|
345
|
+
setCookie('_user_id', id, 365);
|
|
346
|
+
}
|
|
347
|
+
return id;
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**After:**
|
|
352
|
+
```typescript
|
|
353
|
+
import { IdentityManager } from '@sygnl/identity-manager';
|
|
354
|
+
|
|
355
|
+
const identity = new IdentityManager({
|
|
356
|
+
anonymousIdCookieName: '_user_id', // Match your existing cookie name
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const id = identity.ensureAnonymousId();
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### From pixel.source.js (Sygnl)
|
|
363
|
+
|
|
364
|
+
This package extracts and enhances these functions from `pixel.source.js`:
|
|
365
|
+
- `getCookie()` โ `identity.getCookie()`
|
|
366
|
+
- `setCookie()` โ `identity.setCookie()`
|
|
367
|
+
- `generateUUID()` โ Internal (automatic)
|
|
368
|
+
- `ensureAnonymousId()` โ `identity.ensureAnonymousId()`
|
|
369
|
+
- `ensureSessionId()` โ `identity.ensureSessionId()`
|
|
370
|
+
|
|
371
|
+
**Benefits of migration:**
|
|
372
|
+
- โ
TypeScript support
|
|
373
|
+
- โ
Better error handling
|
|
374
|
+
- โ
Configurable options
|
|
375
|
+
- โ
Comprehensive tests
|
|
376
|
+
- โ
SSR safety
|
|
377
|
+
- โ
Maintained package
|
|
378
|
+
|
|
379
|
+
## Browser Compatibility
|
|
380
|
+
|
|
381
|
+
- โ
Chrome/Edge 90+
|
|
382
|
+
- โ
Firefox 88+
|
|
383
|
+
- โ
Safari 14+
|
|
384
|
+
- โ
Node.js 18+ (SSR-safe)
|
|
385
|
+
|
|
386
|
+
**UUID Generation:**
|
|
387
|
+
- Uses `crypto.randomUUID()` in modern browsers
|
|
388
|
+
- Falls back to `Math.random()` for older browsers
|
|
389
|
+
- Both implementations are RFC4122 compliant
|
|
390
|
+
|
|
391
|
+
## Performance
|
|
392
|
+
|
|
393
|
+
- **Bundle Size:** ~2KB minified + gzipped
|
|
394
|
+
- **Runtime:** <1ms for ID generation
|
|
395
|
+
- **Memory:** <1KB per instance
|
|
396
|
+
- **Zero dependencies**
|
|
397
|
+
|
|
398
|
+
## Testing
|
|
399
|
+
|
|
400
|
+
Run the test suite:
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
npm test
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Run with coverage:
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
npm run test:coverage
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Watch mode for development:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
npm run test:watch
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Test Coverage:**
|
|
419
|
+
- 60+ test cases
|
|
420
|
+
- >90% code coverage
|
|
421
|
+
- Edge cases covered
|
|
422
|
+
- Integration tests included
|
|
423
|
+
|
|
424
|
+
## TypeScript
|
|
425
|
+
|
|
426
|
+
Full TypeScript support included:
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
import {
|
|
430
|
+
IdentityManager,
|
|
431
|
+
IdentityManagerOptions,
|
|
432
|
+
PartialIdentityManagerOptions,
|
|
433
|
+
DEFAULT_OPTIONS
|
|
434
|
+
} from '@sygnl/identity-manager';
|
|
435
|
+
|
|
436
|
+
// Type-safe configuration
|
|
437
|
+
const options: PartialIdentityManagerOptions = {
|
|
438
|
+
debug: true,
|
|
439
|
+
anonymousIdTTL: 365,
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const identity = new IdentityManager(options);
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## Contributing
|
|
446
|
+
|
|
447
|
+
Contributions welcome! Please read our [Contributing Guide](CONTRIBUTING.md) first.
|
|
448
|
+
|
|
449
|
+
## License
|
|
450
|
+
|
|
451
|
+
Copyright ยฉ 2025 Edge Foundry, Inc. All rights reserved.
|
|
452
|
+
|
|
453
|
+
## Support
|
|
454
|
+
|
|
455
|
+
- ๐ง Email: support@sygnl.com
|
|
456
|
+
- ๐ Issues: [GitHub Issues](https://github.com/sygnl/identity-manager/issues)
|
|
457
|
+
- ๐ฌ Discussions: [GitHub Discussions](https://github.com/sygnl/identity-manager/discussions)
|
|
458
|
+
|
|
459
|
+
## Related Packages
|
|
460
|
+
|
|
461
|
+
- [@sygnl/event-transport](#) - Analytics event transport layer
|
|
462
|
+
- [@sygnl/page-engagement](#) - Page engagement tracking
|
|
463
|
+
- [@sygnl/event-schema](#) - Event schema builder
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
Made with โค๏ธ by [Sygnl](https://sygnl.com)
|