@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/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
|
+
}
|