@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/dist/index.js ADDED
@@ -0,0 +1,249 @@
1
+ // src/types.ts
2
+ var DEFAULT_OPTIONS = {
3
+ anonymousIdCookieName: "_stid",
4
+ sessionIdCookieName: "_session_id",
5
+ anonymousIdTTL: 365,
6
+ sessionIdTTL: 1,
7
+ cookieDomain: void 0,
8
+ cookiePath: "/",
9
+ cookieSameSite: "Lax",
10
+ cookieSecure: true,
11
+ useLocalStorage: true,
12
+ debug: false
13
+ };
14
+
15
+ // src/IdentityManager.ts
16
+ var IdentityManager = class {
17
+ /**
18
+ * Create a new IdentityManager instance
19
+ * @param options - Configuration options (optional)
20
+ */
21
+ constructor(options) {
22
+ this.options = { ...DEFAULT_OPTIONS, ...options };
23
+ this.log("IdentityManager initialized", this.options);
24
+ }
25
+ /**
26
+ * Configure or update options at runtime
27
+ * @param options - Partial options to merge with existing configuration
28
+ */
29
+ configure(options) {
30
+ this.options = { ...this.options, ...options };
31
+ this.log("Configuration updated", this.options);
32
+ }
33
+ // ==================== Cookie Methods ====================
34
+ /**
35
+ * Get a cookie value by name
36
+ * @param name - Cookie name
37
+ * @returns Cookie value or null if not found
38
+ */
39
+ getCookie(name) {
40
+ if (typeof document === "undefined") {
41
+ this.log("getCookie: document is undefined (SSR context)", { name });
42
+ return null;
43
+ }
44
+ try {
45
+ const cookies = document.cookie.split(";");
46
+ const target = name + "=";
47
+ for (let i = 0; i < cookies.length; i++) {
48
+ const c = cookies[i].trim();
49
+ if (c.indexOf(target) === 0) {
50
+ const value = c.substring(target.length);
51
+ this.log("getCookie: hit", { name, value });
52
+ return value;
53
+ }
54
+ }
55
+ this.log("getCookie: miss", { name });
56
+ return null;
57
+ } catch (error) {
58
+ this.log("getCookie: error", { name, error });
59
+ return null;
60
+ }
61
+ }
62
+ /**
63
+ * Set a cookie with configurable options
64
+ * @param name - Cookie name
65
+ * @param value - Cookie value
66
+ * @param days - Time-to-live in days
67
+ */
68
+ setCookie(name, value, days) {
69
+ if (typeof document === "undefined") {
70
+ this.log("setCookie: document is undefined (SSR context)", { name });
71
+ return;
72
+ }
73
+ try {
74
+ const expires = /* @__PURE__ */ new Date();
75
+ expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1e3);
76
+ let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${this.options.cookiePath}; SameSite=${this.options.cookieSameSite}`;
77
+ if (this.options.cookieSecure) {
78
+ cookieString += "; Secure";
79
+ }
80
+ if (this.options.cookieDomain) {
81
+ cookieString += `; Domain=${this.options.cookieDomain}`;
82
+ }
83
+ document.cookie = cookieString;
84
+ this.log("setCookie: success", {
85
+ name,
86
+ value,
87
+ expires: expires.toISOString(),
88
+ ttlSeconds: days * 86400
89
+ });
90
+ } catch (error) {
91
+ this.log("setCookie: error", { name, error });
92
+ }
93
+ }
94
+ // ==================== localStorage Methods ====================
95
+ /**
96
+ * Get a value from localStorage
97
+ * @param key - Storage key
98
+ * @returns Stored value or null
99
+ */
100
+ getLocalStorage(key) {
101
+ if (!this.options.useLocalStorage) {
102
+ return null;
103
+ }
104
+ if (typeof localStorage === "undefined") {
105
+ this.log("getLocalStorage: localStorage is undefined (SSR context)", { key });
106
+ return null;
107
+ }
108
+ try {
109
+ const value = localStorage.getItem(key);
110
+ this.log("getLocalStorage: " + (value ? "hit" : "miss"), { key, value });
111
+ return value;
112
+ } catch (error) {
113
+ this.log("getLocalStorage: error (private browsing?)", { key, error });
114
+ return null;
115
+ }
116
+ }
117
+ /**
118
+ * Set a value in localStorage
119
+ * @param key - Storage key
120
+ * @param value - Value to store
121
+ */
122
+ setLocalStorage(key, value) {
123
+ if (!this.options.useLocalStorage) {
124
+ return;
125
+ }
126
+ if (typeof localStorage === "undefined") {
127
+ this.log("setLocalStorage: localStorage is undefined (SSR context)", { key });
128
+ return;
129
+ }
130
+ try {
131
+ localStorage.setItem(key, value);
132
+ this.log("setLocalStorage: success", { key, value });
133
+ } catch (error) {
134
+ this.log("setLocalStorage: error (quota exceeded?)", { key, error });
135
+ }
136
+ }
137
+ // ==================== UUID Generation ====================
138
+ /**
139
+ * Generate a UUID v4
140
+ * Uses crypto.randomUUID() if available, falls back to Math.random()
141
+ * @returns UUID string
142
+ */
143
+ generateUUID() {
144
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
145
+ try {
146
+ const uuid2 = crypto.randomUUID();
147
+ this.log("generateUUID: crypto.randomUUID()", { uuid: uuid2 });
148
+ return uuid2;
149
+ } catch (error) {
150
+ this.log("generateUUID: crypto.randomUUID() failed, falling back", { error });
151
+ }
152
+ }
153
+ const uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
154
+ const r = Math.random() * 16 | 0;
155
+ const v = c === "x" ? r : r & 3 | 8;
156
+ return v.toString(16);
157
+ });
158
+ this.log("generateUUID: Math.random() fallback", { uuid });
159
+ return uuid;
160
+ }
161
+ // ==================== Anonymous ID Methods ====================
162
+ /**
163
+ * Get the current anonymous ID without creating a new one
164
+ * @returns Anonymous ID or null if not found
165
+ */
166
+ getAnonymousId() {
167
+ const cookieValue = this.getCookie(this.options.anonymousIdCookieName);
168
+ if (cookieValue) {
169
+ return cookieValue;
170
+ }
171
+ const storageValue = this.getLocalStorage(this.options.anonymousIdCookieName);
172
+ if (storageValue) {
173
+ this.setCookie(
174
+ this.options.anonymousIdCookieName,
175
+ storageValue,
176
+ this.options.anonymousIdTTL
177
+ );
178
+ return storageValue;
179
+ }
180
+ return null;
181
+ }
182
+ /**
183
+ * Get or create an anonymous ID
184
+ * @returns Anonymous ID (always returns a value)
185
+ */
186
+ ensureAnonymousId() {
187
+ let anonymousId = this.getCookie(this.options.anonymousIdCookieName);
188
+ if (!anonymousId) {
189
+ anonymousId = this.getLocalStorage(this.options.anonymousIdCookieName);
190
+ if (!anonymousId) {
191
+ anonymousId = this.generateUUID();
192
+ this.log("ensureAnonymousId: generated new ID", { anonymousId });
193
+ } else {
194
+ this.log("ensureAnonymousId: restored from localStorage", { anonymousId });
195
+ }
196
+ this.setCookie(
197
+ this.options.anonymousIdCookieName,
198
+ anonymousId,
199
+ this.options.anonymousIdTTL
200
+ );
201
+ this.setLocalStorage(this.options.anonymousIdCookieName, anonymousId);
202
+ } else {
203
+ this.log("ensureAnonymousId: found in cookie", { anonymousId });
204
+ }
205
+ return anonymousId;
206
+ }
207
+ // ==================== Session ID Methods ====================
208
+ /**
209
+ * Get the current session ID without creating a new one
210
+ * @returns Session ID or null if not found
211
+ */
212
+ getSessionId() {
213
+ return this.getCookie(this.options.sessionIdCookieName);
214
+ }
215
+ /**
216
+ * Get or create a session ID
217
+ * @returns Session ID (always returns a value)
218
+ */
219
+ ensureSessionId() {
220
+ let sessionId = this.getCookie(this.options.sessionIdCookieName);
221
+ if (!sessionId) {
222
+ sessionId = `sess_${Date.now()}`;
223
+ this.setCookie(
224
+ this.options.sessionIdCookieName,
225
+ sessionId,
226
+ this.options.sessionIdTTL
227
+ );
228
+ this.log("ensureSessionId: generated new session", { sessionId });
229
+ } else {
230
+ this.log("ensureSessionId: found existing session", { sessionId });
231
+ }
232
+ return sessionId;
233
+ }
234
+ // ==================== Debug Logging ====================
235
+ /**
236
+ * Log debug messages to console if debug mode is enabled
237
+ * @param message - Log message
238
+ * @param data - Additional data to log
239
+ */
240
+ log(message, data) {
241
+ if (this.options.debug && typeof console !== "undefined") {
242
+ console.log(`[IdentityManager] ${message}`, data || "");
243
+ }
244
+ }
245
+ };
246
+
247
+ export { DEFAULT_OPTIONS, IdentityManager };
248
+ //# sourceMappingURL=index.js.map
249
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/IdentityManager.ts"],"names":["uuid"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Configuration options for IdentityManager\n */\nexport interface IdentityManagerOptions {\n /**\n * Cookie name for anonymous ID storage\n * @default '_stid'\n */\n anonymousIdCookieName: string;\n\n /**\n * Cookie name for session ID storage\n * @default '_session_id'\n */\n sessionIdCookieName: string;\n\n /**\n * Anonymous ID time-to-live in days\n * @default 365\n */\n anonymousIdTTL: number;\n\n /**\n * Session ID time-to-live in days\n * @default 1\n */\n sessionIdTTL: number;\n\n /**\n * Cookie domain (optional, defaults to current domain)\n * Use '.example.com' for subdomain sharing\n */\n cookieDomain?: string;\n\n /**\n * Cookie path\n * @default '/'\n */\n cookiePath: string;\n\n /**\n * Cookie SameSite attribute\n * @default 'Lax'\n */\n cookieSameSite: 'Strict' | 'Lax' | 'None';\n\n /**\n * Cookie Secure flag (requires HTTPS)\n * @default true\n */\n cookieSecure: boolean;\n\n /**\n * Enable localStorage fallback when cookies fail\n * @default true\n */\n useLocalStorage: boolean;\n\n /**\n * Enable debug logging to console\n * @default false\n */\n debug: boolean;\n}\n\n/**\n * Partial configuration for runtime updates\n */\nexport type PartialIdentityManagerOptions = Partial<IdentityManagerOptions>;\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_OPTIONS: IdentityManagerOptions = {\n anonymousIdCookieName: '_stid',\n sessionIdCookieName: '_session_id',\n anonymousIdTTL: 365,\n sessionIdTTL: 1,\n cookieDomain: undefined,\n cookiePath: '/',\n cookieSameSite: 'Lax',\n cookieSecure: true,\n useLocalStorage: true,\n debug: false,\n};\n","import {\n IdentityManagerOptions,\n PartialIdentityManagerOptions,\n DEFAULT_OPTIONS,\n} from './types';\n\n/**\n * IdentityManager - Manages anonymous user identity and session tracking\n * \n * Features:\n * - Persistent anonymous ID with cookie + localStorage fallback\n * - Temporary session ID management\n * - Configurable TTL for both identity types\n * - SSR-safe (gracefully handles missing document/window)\n * - Zero dependencies\n * \n * @example\n * ```typescript\n * const identity = new IdentityManager({ debug: true });\n * const userId = identity.ensureAnonymousId();\n * const sessionId = identity.ensureSessionId();\n * ```\n */\nexport class IdentityManager {\n private options: IdentityManagerOptions;\n\n /**\n * Create a new IdentityManager instance\n * @param options - Configuration options (optional)\n */\n constructor(options?: PartialIdentityManagerOptions) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.log('IdentityManager initialized', this.options);\n }\n\n /**\n * Configure or update options at runtime\n * @param options - Partial options to merge with existing configuration\n */\n public configure(options: PartialIdentityManagerOptions): void {\n this.options = { ...this.options, ...options };\n this.log('Configuration updated', this.options);\n }\n\n // ==================== Cookie Methods ====================\n\n /**\n * Get a cookie value by name\n * @param name - Cookie name\n * @returns Cookie value or null if not found\n */\n public getCookie(name: string): string | null {\n // SSR safety check\n if (typeof document === 'undefined') {\n this.log('getCookie: document is undefined (SSR context)', { name });\n return null;\n }\n\n try {\n const cookies = document.cookie.split(';');\n const target = name + '=';\n \n for (let i = 0; i < cookies.length; i++) {\n const c = cookies[i].trim();\n if (c.indexOf(target) === 0) {\n const value = c.substring(target.length);\n this.log('getCookie: hit', { name, value });\n return value;\n }\n }\n \n this.log('getCookie: miss', { name });\n return null;\n } catch (error) {\n this.log('getCookie: error', { name, error });\n return null;\n }\n }\n\n /**\n * Set a cookie with configurable options\n * @param name - Cookie name\n * @param value - Cookie value\n * @param days - Time-to-live in days\n */\n public setCookie(name: string, value: string, days: number): void {\n // SSR safety check\n if (typeof document === 'undefined') {\n this.log('setCookie: document is undefined (SSR context)', { name });\n return;\n }\n\n try {\n const expires = new Date();\n expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);\n\n let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${this.options.cookiePath}; SameSite=${this.options.cookieSameSite}`;\n\n if (this.options.cookieSecure) {\n cookieString += '; Secure';\n }\n\n if (this.options.cookieDomain) {\n cookieString += `; Domain=${this.options.cookieDomain}`;\n }\n\n document.cookie = cookieString;\n\n this.log('setCookie: success', {\n name,\n value,\n expires: expires.toISOString(),\n ttlSeconds: days * 86400,\n });\n } catch (error) {\n this.log('setCookie: error', { name, error });\n }\n }\n\n // ==================== localStorage Methods ====================\n\n /**\n * Get a value from localStorage\n * @param key - Storage key\n * @returns Stored value or null\n */\n private getLocalStorage(key: string): string | null {\n if (!this.options.useLocalStorage) {\n return null;\n }\n\n // SSR safety check\n if (typeof localStorage === 'undefined') {\n this.log('getLocalStorage: localStorage is undefined (SSR context)', { key });\n return null;\n }\n\n try {\n const value = localStorage.getItem(key);\n this.log('getLocalStorage: ' + (value ? 'hit' : 'miss'), { key, value });\n return value;\n } catch (error) {\n this.log('getLocalStorage: error (private browsing?)', { key, error });\n return null;\n }\n }\n\n /**\n * Set a value in localStorage\n * @param key - Storage key\n * @param value - Value to store\n */\n private setLocalStorage(key: string, value: string): void {\n if (!this.options.useLocalStorage) {\n return;\n }\n\n // SSR safety check\n if (typeof localStorage === 'undefined') {\n this.log('setLocalStorage: localStorage is undefined (SSR context)', { key });\n return;\n }\n\n try {\n localStorage.setItem(key, value);\n this.log('setLocalStorage: success', { key, value });\n } catch (error) {\n this.log('setLocalStorage: error (quota exceeded?)', { key, error });\n }\n }\n\n // ==================== UUID Generation ====================\n\n /**\n * Generate a UUID v4\n * Uses crypto.randomUUID() if available, falls back to Math.random()\n * @returns UUID string\n */\n private generateUUID(): string {\n // Modern browsers with crypto.randomUUID()\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n try {\n const uuid = crypto.randomUUID();\n this.log('generateUUID: crypto.randomUUID()', { uuid });\n return uuid;\n } catch (error) {\n this.log('generateUUID: crypto.randomUUID() failed, falling back', { error });\n }\n }\n\n // Fallback to Math.random() implementation (RFC4122 compliant)\n const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n\n this.log('generateUUID: Math.random() fallback', { uuid });\n return uuid;\n }\n\n // ==================== Anonymous ID Methods ====================\n\n /**\n * Get the current anonymous ID without creating a new one\n * @returns Anonymous ID or null if not found\n */\n public getAnonymousId(): string | null {\n // Try cookie first\n const cookieValue = this.getCookie(this.options.anonymousIdCookieName);\n if (cookieValue) {\n return cookieValue;\n }\n\n // Try localStorage fallback\n const storageValue = this.getLocalStorage(this.options.anonymousIdCookieName);\n if (storageValue) {\n // Restore to cookie if found in localStorage\n this.setCookie(\n this.options.anonymousIdCookieName,\n storageValue,\n this.options.anonymousIdTTL\n );\n return storageValue;\n }\n\n return null;\n }\n\n /**\n * Get or create an anonymous ID\n * @returns Anonymous ID (always returns a value)\n */\n public ensureAnonymousId(): string {\n // Try to get existing ID\n let anonymousId = this.getCookie(this.options.anonymousIdCookieName);\n\n if (!anonymousId) {\n // Try localStorage fallback\n anonymousId = this.getLocalStorage(this.options.anonymousIdCookieName);\n\n if (!anonymousId) {\n // Generate new UUID\n anonymousId = this.generateUUID();\n this.log('ensureAnonymousId: generated new ID', { anonymousId });\n } else {\n this.log('ensureAnonymousId: restored from localStorage', { anonymousId });\n }\n\n // Save to both cookie and localStorage\n this.setCookie(\n this.options.anonymousIdCookieName,\n anonymousId,\n this.options.anonymousIdTTL\n );\n this.setLocalStorage(this.options.anonymousIdCookieName, anonymousId);\n } else {\n this.log('ensureAnonymousId: found in cookie', { anonymousId });\n }\n\n return anonymousId;\n }\n\n // ==================== Session ID Methods ====================\n\n /**\n * Get the current session ID without creating a new one\n * @returns Session ID or null if not found\n */\n public getSessionId(): string | null {\n return this.getCookie(this.options.sessionIdCookieName);\n }\n\n /**\n * Get or create a session ID\n * @returns Session ID (always returns a value)\n */\n public ensureSessionId(): string {\n let sessionId = this.getCookie(this.options.sessionIdCookieName);\n\n if (!sessionId) {\n sessionId = `sess_${Date.now()}`;\n this.setCookie(\n this.options.sessionIdCookieName,\n sessionId,\n this.options.sessionIdTTL\n );\n this.log('ensureSessionId: generated new session', { sessionId });\n } else {\n this.log('ensureSessionId: found existing session', { sessionId });\n }\n\n return sessionId;\n }\n\n // ==================== Debug Logging ====================\n\n /**\n * Log debug messages to console if debug mode is enabled\n * @param message - Log message\n * @param data - Additional data to log\n */\n private log(message: string, data?: unknown): void {\n if (this.options.debug && typeof console !== 'undefined') {\n console.log(`[IdentityManager] ${message}`, data || '');\n }\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@sygnl/identity-manager",
3
+ "version": "1.0.1",
4
+ "description": "Lightweight identity and session management with cookie + localStorage fallback",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "keywords": [
21
+ "identity",
22
+ "session",
23
+ "cookie",
24
+ "analytics",
25
+ "tracking",
26
+ "anonymous-id",
27
+ "localStorage",
28
+ "uuid"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsup",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "test:coverage": "vitest run --coverage",
35
+ "typecheck": "tsc --noEmit",
36
+ "prepublishOnly": "npm run build"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^20.10.0",
40
+ "tsup": "^8.0.1",
41
+ "typescript": "^5.3.3",
42
+ "vitest": "^1.0.4",
43
+ "@vitest/coverage-v8": "^1.0.4",
44
+ "happy-dom": "^12.10.3"
45
+ },
46
+ "engines": {
47
+ "node": ">=18"
48
+ },
49
+ "author": "Edge Foundry, Inc.",
50
+ "license": "Apache-2.0",
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/sygnl/identity-manager"
54
+ }
55
+ }