@formo/analytics 1.27.0 → 1.28.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.
Files changed (74) hide show
  1. package/dist/cjs/src/FormoAnalytics.d.ts +69 -12
  2. package/dist/cjs/src/FormoAnalytics.js +273 -147
  3. package/dist/cjs/src/event/EventFactory.d.ts +10 -2
  4. package/dist/cjs/src/event/EventFactory.js +32 -21
  5. package/dist/cjs/src/index.d.ts +4 -0
  6. package/dist/cjs/src/index.js +6 -0
  7. package/dist/cjs/src/privy/index.d.ts +9 -0
  8. package/dist/cjs/src/privy/index.js +12 -0
  9. package/dist/cjs/src/privy/types.d.ts +176 -0
  10. package/dist/cjs/src/privy/types.js +12 -0
  11. package/dist/cjs/src/privy/utils.d.ts +32 -0
  12. package/dist/cjs/src/privy/utils.js +191 -0
  13. package/dist/cjs/src/session/index.js +2 -1
  14. package/dist/cjs/src/solana/SolanaAdapter.d.ts +211 -0
  15. package/dist/cjs/src/solana/SolanaAdapter.js +975 -0
  16. package/dist/cjs/src/solana/SolanaManager.d.ts +24 -0
  17. package/dist/cjs/src/solana/SolanaManager.js +80 -0
  18. package/dist/cjs/src/solana/address.d.ts +72 -0
  19. package/dist/cjs/src/solana/address.js +176 -0
  20. package/dist/cjs/src/solana/index.d.ts +13 -0
  21. package/dist/cjs/src/solana/index.js +32 -0
  22. package/dist/cjs/src/solana/types.d.ts +206 -0
  23. package/dist/cjs/src/solana/types.js +80 -0
  24. package/dist/cjs/src/types/base.d.ts +17 -0
  25. package/dist/cjs/src/types/events.d.ts +4 -3
  26. package/dist/cjs/src/utils/address.d.ts +21 -0
  27. package/dist/cjs/src/utils/address.js +48 -1
  28. package/dist/cjs/src/utils/builderCode.d.ts +30 -0
  29. package/dist/cjs/src/utils/builderCode.js +143 -0
  30. package/dist/cjs/src/utils/index.d.ts +1 -0
  31. package/dist/cjs/src/utils/index.js +1 -0
  32. package/dist/cjs/src/version.d.ts +1 -1
  33. package/dist/cjs/src/version.js +1 -1
  34. package/dist/cjs/src/wagmi/WagmiEventHandler.js +13 -15
  35. package/dist/cjs/src/wagmi/utils.d.ts +5 -0
  36. package/dist/cjs/src/wagmi/utils.js +20 -0
  37. package/dist/esm/src/FormoAnalytics.d.ts +69 -12
  38. package/dist/esm/src/FormoAnalytics.js +274 -148
  39. package/dist/esm/src/event/EventFactory.d.ts +10 -2
  40. package/dist/esm/src/event/EventFactory.js +34 -23
  41. package/dist/esm/src/index.d.ts +4 -0
  42. package/dist/esm/src/index.js +3 -0
  43. package/dist/esm/src/privy/index.d.ts +9 -0
  44. package/dist/esm/src/privy/index.js +8 -0
  45. package/dist/esm/src/privy/types.d.ts +176 -0
  46. package/dist/esm/src/privy/types.js +11 -0
  47. package/dist/esm/src/privy/utils.d.ts +32 -0
  48. package/dist/esm/src/privy/utils.js +188 -0
  49. package/dist/esm/src/session/index.js +2 -1
  50. package/dist/esm/src/solana/SolanaAdapter.d.ts +211 -0
  51. package/dist/esm/src/solana/SolanaAdapter.js +972 -0
  52. package/dist/esm/src/solana/SolanaManager.d.ts +24 -0
  53. package/dist/esm/src/solana/SolanaManager.js +77 -0
  54. package/dist/esm/src/solana/address.d.ts +72 -0
  55. package/dist/esm/src/solana/address.js +167 -0
  56. package/dist/esm/src/solana/index.d.ts +13 -0
  57. package/dist/esm/src/solana/index.js +13 -0
  58. package/dist/esm/src/solana/types.d.ts +206 -0
  59. package/dist/esm/src/solana/types.js +74 -0
  60. package/dist/esm/src/types/base.d.ts +17 -0
  61. package/dist/esm/src/types/events.d.ts +4 -3
  62. package/dist/esm/src/utils/address.d.ts +21 -0
  63. package/dist/esm/src/utils/address.js +45 -0
  64. package/dist/esm/src/utils/builderCode.d.ts +30 -0
  65. package/dist/esm/src/utils/builderCode.js +140 -0
  66. package/dist/esm/src/utils/index.d.ts +1 -0
  67. package/dist/esm/src/utils/index.js +1 -0
  68. package/dist/esm/src/version.d.ts +1 -1
  69. package/dist/esm/src/version.js +1 -1
  70. package/dist/esm/src/wagmi/WagmiEventHandler.js +14 -16
  71. package/dist/esm/src/wagmi/utils.d.ts +5 -0
  72. package/dist/esm/src/wagmi/utils.js +19 -0
  73. package/dist/index.umd.min.js +1 -1
  74. package/package.json +15 -3
@@ -4,6 +4,14 @@ declare class EventFactory implements IEventFactory {
4
4
  private options?;
5
5
  private compiledPathPattern?;
6
6
  constructor(options?: Options);
7
+ /**
8
+ * Validate an address for both EVM and Solana chains.
9
+ * Uses chainId for strict validation when available.
10
+ * @param address The address to validate
11
+ * @param chainId Optional chain ID for strict chain-specific validation
12
+ * @returns The validated address or null if invalid
13
+ */
14
+ private validateEventAddress;
7
15
  private getTimezone;
8
16
  private getLocation;
9
17
  private getLanguage;
@@ -22,12 +30,12 @@ declare class EventFactory implements IEventFactory {
22
30
  private getEnrichedEvent;
23
31
  generatePageEvent(category?: string, name?: string, properties?: IFormoEventProperties, context?: IFormoEventContext): Promise<IFormoEvent>;
24
32
  generateDetectWalletEvent(providerName: string, rdns: string, properties?: IFormoEventProperties, context?: IFormoEventContext): Promise<IFormoEvent>;
25
- generateIdentifyEvent(providerName: string, rdns: string, address: Nullable<Address>, userId?: Nullable<string>, properties?: IFormoEventProperties, context?: IFormoEventContext): Promise<IFormoEvent>;
33
+ generateIdentifyEvent(providerName?: string, rdns?: string, address?: Nullable<Address>, userId?: Nullable<string>, properties?: IFormoEventProperties, context?: IFormoEventContext): Promise<IFormoEvent>;
26
34
  generateConnectEvent(chainId: ChainID, address: Address, properties?: IFormoEventProperties, context?: IFormoEventContext): Promise<IFormoEvent>;
27
35
  generateDisconnectEvent(chainId?: ChainID, address?: Address, properties?: IFormoEventProperties, context?: IFormoEventContext): Promise<IFormoEvent>;
28
36
  generateChainChangedEvent(chainId: ChainID, address: Address, properties?: IFormoEventProperties, context?: IFormoEventContext): Promise<IFormoEvent>;
29
37
  generateSignatureEvent(status: SignatureStatus, chainId: ChainID, address: Address, message: string, signatureHash?: string, properties?: IFormoEventProperties, context?: IFormoEventContext): Promise<IFormoEvent>;
30
- generateTransactionEvent(status: TransactionStatus, chainId: ChainID, address: Address, data?: string, to?: string, value?: string, transactionHash?: string, function_name?: string, function_args?: Record<string, unknown>, properties?: IFormoEventProperties, context?: IFormoEventContext): Promise<IFormoEvent>;
38
+ generateTransactionEvent(status: TransactionStatus, chainId: ChainID, address: Address, data?: string, to?: string, value?: string, transactionHash?: string, function_name?: string, function_args?: Record<string, unknown>, builder_codes?: string, properties?: IFormoEventProperties, context?: IFormoEventContext): Promise<IFormoEvent>;
31
39
  generateTrackEvent(event: string, properties?: IFormoEventProperties, context?: IFormoEventContext): Promise<IFormoEvent>;
32
40
  create(event: APIEvent, address?: Address, userId?: string): Promise<IFormoEvent>;
33
41
  }
@@ -46,8 +46,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
46
46
  }
47
47
  };
48
48
  import { COUNTRY_LIST, LOCAL_ANONYMOUS_ID_KEY, SESSION_TRAFFIC_SOURCE_KEY, } from "../constants";
49
- import { toChecksumAddress, toSnakeCase } from "../utils";
50
- import { getValidAddress } from "../utils/address";
49
+ import { toSnakeCase } from "../utils";
50
+ import { validateAddress } from "../utils/address";
51
51
  import { getCurrentTimeFormatted } from "../utils/timestamp";
52
52
  import { isUndefined } from "../validators";
53
53
  import { logger } from "../logger";
@@ -192,6 +192,19 @@ var EventFactory = /** @class */ (function () {
192
192
  }
193
193
  }
194
194
  }
195
+ /**
196
+ * Validate an address for both EVM and Solana chains.
197
+ * Uses chainId for strict validation when available.
198
+ * @param address The address to validate
199
+ * @param chainId Optional chain ID for strict chain-specific validation
200
+ * @returns The validated address or null if invalid
201
+ */
202
+ EventFactory.prototype.validateEventAddress = function (address, chainId) {
203
+ if (!address) {
204
+ return null;
205
+ }
206
+ return validateAddress(address, chainId) || null;
207
+ };
195
208
  EventFactory.prototype.getTimezone = function () {
196
209
  try {
197
210
  return Intl.DateTimeFormat().resolvedOptions().timeZone;
@@ -274,15 +287,16 @@ var EventFactory = /** @class */ (function () {
274
287
  };
275
288
  EventFactory.prototype.getEnrichedEvent = function (formoEvent, context) {
276
289
  return __awaiter(this, void 0, void 0, function () {
277
- var commonEventData, validAddress, processedEvent;
290
+ var commonEventData, eventChainId, validAddress, processedEvent;
278
291
  var _a;
279
- return __generator(this, function (_b) {
280
- switch (_b.label) {
292
+ var _b;
293
+ return __generator(this, function (_c) {
294
+ switch (_c.label) {
281
295
  case 0:
282
296
  _a = {};
283
297
  return [4 /*yield*/, this.generateContext(context)];
284
298
  case 1:
285
- commonEventData = (_a.context = _b.sent(),
299
+ commonEventData = (_a.context = _c.sent(),
286
300
  _a.original_timestamp = getCurrentTimeFormatted(),
287
301
  _a.user_id = formoEvent.user_id,
288
302
  _a.type = formoEvent.type,
@@ -290,13 +304,9 @@ var EventFactory = /** @class */ (function () {
290
304
  _a.version = VERSION,
291
305
  _a);
292
306
  commonEventData.anonymous_id = generateAnonymousId(LOCAL_ANONYMOUS_ID_KEY);
293
- validAddress = getValidAddress(formoEvent.address);
294
- if (validAddress) {
295
- commonEventData.address = toChecksumAddress(validAddress);
296
- }
297
- else {
298
- commonEventData.address = null;
299
- }
307
+ eventChainId = (_b = formoEvent.properties) === null || _b === void 0 ? void 0 : _b.chainId;
308
+ validAddress = this.validateEventAddress(formoEvent.address, eventChainId);
309
+ commonEventData.address = validAddress;
300
310
  processedEvent = mergeDeepRight(formoEvent, commonEventData);
301
311
  if (processedEvent.event === undefined) {
302
312
  processedEvent.event = null;
@@ -342,7 +352,7 @@ var EventFactory = /** @class */ (function () {
342
352
  var identifyEvent;
343
353
  return __generator(this, function (_a) {
344
354
  identifyEvent = {
345
- properties: __assign({ providerName: providerName, rdns: rdns }, properties),
355
+ properties: __assign(__assign(__assign({}, (providerName !== undefined && { providerName: providerName })), (rdns !== undefined && { rdns: rdns })), properties),
346
356
  user_id: userId,
347
357
  address: address,
348
358
  type: "identify",
@@ -403,12 +413,12 @@ var EventFactory = /** @class */ (function () {
403
413
  });
404
414
  });
405
415
  };
406
- EventFactory.prototype.generateTransactionEvent = function (status, chainId, address, data, to, value, transactionHash, function_name, function_args, properties, context) {
416
+ EventFactory.prototype.generateTransactionEvent = function (status, chainId, address, data, to, value, transactionHash, function_name, function_args, builder_codes, properties, context) {
407
417
  return __awaiter(this, void 0, void 0, function () {
408
418
  var transactionEvent;
409
419
  return __generator(this, function (_a) {
410
420
  transactionEvent = {
411
- properties: __assign(__assign(__assign(__assign(__assign(__assign(__assign({ status: status, chainId: chainId }, (data && { data: data })), (to && { to: to })), (value && { value: value })), (transactionHash && { transactionHash: transactionHash })), (function_name && { function_name: function_name })), (function_args && { function_args: function_args })), properties),
421
+ properties: __assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign({ status: status, chainId: chainId }, (data && { data: data })), (to && { to: to })), (value && { value: value })), (transactionHash && { transactionHash: transactionHash })), (function_name && { function_name: function_name })), (function_args && { function_args: function_args })), (builder_codes && { builder_codes: builder_codes })), properties),
412
422
  address: address,
413
423
  type: "transaction",
414
424
  };
@@ -441,7 +451,7 @@ var EventFactory = /** @class */ (function () {
441
451
  // Returns an event with type, context, properties, and common properties
442
452
  EventFactory.prototype.create = function (event, address, userId) {
443
453
  return __awaiter(this, void 0, void 0, function () {
444
- var formoEvent, _a, validAddress;
454
+ var formoEvent, _a, chainId;
445
455
  return __generator(this, function (_b) {
446
456
  switch (_b.label) {
447
457
  case 0:
@@ -487,7 +497,7 @@ var EventFactory = /** @class */ (function () {
487
497
  case 14:
488
498
  formoEvent = _b.sent();
489
499
  return [3 /*break*/, 19];
490
- case 15: return [4 /*yield*/, this.generateTransactionEvent(event.status, event.chainId, event.address, event.data, event.to, event.value, event.transactionHash, event.function_name, event.function_args, event.properties, event.context)];
500
+ case 15: return [4 /*yield*/, this.generateTransactionEvent(event.status, event.chainId, event.address, event.data, event.to, event.value, event.transactionHash, event.function_name, event.function_args, event.builder_codes, event.properties, event.context)];
491
501
  case 16:
492
502
  formoEvent = _b.sent();
493
503
  return [3 /*break*/, 19];
@@ -497,11 +507,12 @@ var EventFactory = /** @class */ (function () {
497
507
  return [3 /*break*/, 19];
498
508
  case 19:
499
509
  // Set address if not already set by the specific event generator
500
- if (formoEvent.address === undefined || formoEvent.address === null) {
501
- validAddress = getValidAddress(address);
502
- formoEvent.address = validAddress
503
- ? toChecksumAddress(validAddress)
504
- : null;
510
+ // Uses chainId for strict chain-specific validation
511
+ // Skip backfill for identify events to prevent stale address being used
512
+ if ((formoEvent.address === undefined || formoEvent.address === null) &&
513
+ event.type !== "identify") {
514
+ chainId = 'chainId' in event ? event.chainId : undefined;
515
+ formoEvent.address = this.validateEventAddress(address, chainId);
505
516
  }
506
517
  formoEvent.user_id = userId || null;
507
518
  return [2 /*return*/, formoEvent];
@@ -1,4 +1,8 @@
1
1
  export * from "./FormoAnalyticsProvider";
2
2
  export * from "./FormoAnalytics";
3
3
  export * from "./types";
4
+ export { parsePrivyProperties } from "./privy";
5
+ export type { PrivyUser, PrivyLinkedAccount, PrivyAccountType, PrivyProfileProperties, PrivyWalletInfo } from "./privy";
6
+ export { SolanaManager } from "./solana";
7
+ export type { SolanaOptions, SolanaCluster, ISolanaAdapter, SolanaWalletContext, SolanaPublicKey, SolanaConnection, } from "./solana";
4
8
  //# sourceMappingURL=index.d.ts.map
@@ -2,6 +2,9 @@ import { formofy } from "./initialization";
2
2
  export * from "./FormoAnalyticsProvider";
3
3
  export * from "./FormoAnalytics";
4
4
  export * from "./types";
5
+ export { parsePrivyProperties } from "./privy";
6
+ // Solana integration exports
7
+ export { SolanaManager } from "./solana";
5
8
  if (typeof window !== "undefined")
6
9
  window.formofy = formofy;
7
10
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Privy integration module
3
+ *
4
+ * Provides utilities for enriching wallet profiles with Privy user data.
5
+ * This module exports the property extraction utility and related types.
6
+ */
7
+ export { parsePrivyProperties } from "./utils";
8
+ export type { PrivyUser, PrivyLinkedAccount, PrivyAccountType, PrivyProfileProperties, PrivyWalletInfo, } from "./types";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Privy integration module
3
+ *
4
+ * Provides utilities for enriching wallet profiles with Privy user data.
5
+ * This module exports the property extraction utility and related types.
6
+ */
7
+ export { parsePrivyProperties } from "./utils";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Privy-specific type definitions for user profile enrichment
3
+ *
4
+ * These types provide TypeScript interfaces for Privy user objects
5
+ * from the Privy React SDK (`usePrivy()` hook).
6
+ *
7
+ * Based on the Privy user object structure:
8
+ * https://docs.privy.io/user-management/users/the-user-object
9
+ */
10
+ /**
11
+ * Valid Privy linked account type strings.
12
+ */
13
+ export type PrivyAccountType = "email" | "phone" | "wallet" | "smart_wallet" | "farcaster" | "telegram" | "apple_oauth" | "discord_oauth" | "github_oauth" | "google_oauth" | "instagram_oauth" | "linkedin_oauth" | "spotify_oauth" | "tiktok_oauth" | "twitter_oauth" | "twitch_oauth" | "line_oauth" | "custom_auth" | "passkey" | "cross_app" | "guest" | string;
14
+ /**
15
+ * A linked account entry from the Privy user object.
16
+ * Each linked account has a `type` discriminator and type-specific fields.
17
+ */
18
+ export interface PrivyLinkedAccount {
19
+ type: PrivyAccountType;
20
+ address?: string | null;
21
+ number?: string | null;
22
+ username?: string | null;
23
+ name?: string | null;
24
+ displayName?: string | null;
25
+ subject?: string | null;
26
+ email?: string | null;
27
+ chainType?: string | null;
28
+ walletClient?: string | null;
29
+ walletClientType?: string | null;
30
+ connectorType?: string | null;
31
+ delegated?: boolean;
32
+ fid?: number | null;
33
+ ownerAddress?: string | null;
34
+ bio?: string | null;
35
+ pfp?: string | null;
36
+ url?: string | null;
37
+ signerPublicKey?: string | null;
38
+ telegramUserId?: string | null;
39
+ firstName?: string | null;
40
+ lastName?: string | null;
41
+ firstVerifiedAt?: Date | null;
42
+ latestVerifiedAt?: Date | null;
43
+ }
44
+ /**
45
+ * Privy user object as returned by the Privy React SDK.
46
+ *
47
+ * Convenience accessors: user.email, user.phone, user.wallet, user.discord,
48
+ * user.twitter, user.farcaster, user.github, user.google, user.linkedin,
49
+ * user.apple, user.instagram, user.spotify, user.tiktok, user.telegram, user.line
50
+ */
51
+ export interface PrivyUser {
52
+ /** Privy user ID in DID format (e.g., "did:privy:cm3np...") */
53
+ id: string;
54
+ /** Account creation timestamp */
55
+ createdAt?: Date;
56
+ /** All linked accounts */
57
+ linkedAccounts?: PrivyLinkedAccount[];
58
+ /** Optional custom metadata */
59
+ customMetadata?: Record<string, unknown>;
60
+ email?: {
61
+ address: string;
62
+ };
63
+ phone?: {
64
+ number: string;
65
+ };
66
+ wallet?: {
67
+ address: string;
68
+ chainType?: string;
69
+ walletClient?: string;
70
+ walletClientType?: string;
71
+ connectorType?: string;
72
+ };
73
+ google?: {
74
+ subject: string;
75
+ email: string;
76
+ name: string | null;
77
+ };
78
+ discord?: {
79
+ subject: string;
80
+ username: string | null;
81
+ email: string | null;
82
+ };
83
+ twitter?: {
84
+ subject: string;
85
+ username: string | null;
86
+ name: string | null;
87
+ profilePictureUrl: string | null;
88
+ };
89
+ farcaster?: {
90
+ fid: number | null;
91
+ ownerAddress: string;
92
+ username: string | null;
93
+ displayName: string | null;
94
+ bio: string | null;
95
+ pfp: string | null;
96
+ };
97
+ github?: {
98
+ subject: string;
99
+ username: string | null;
100
+ name: string | null;
101
+ };
102
+ linkedin?: {
103
+ subject: string;
104
+ name: string | null;
105
+ email: string | null;
106
+ vanityName: string | null;
107
+ };
108
+ apple?: {
109
+ subject: string;
110
+ email: string;
111
+ };
112
+ instagram?: {
113
+ subject: string;
114
+ username: string | null;
115
+ };
116
+ spotify?: {
117
+ subject: string;
118
+ email: string | null;
119
+ name: string | null;
120
+ };
121
+ tiktok?: {
122
+ subject: string;
123
+ username: string | null;
124
+ name: string | null;
125
+ };
126
+ line?: {
127
+ subject: string;
128
+ name: string | null;
129
+ email: string | null;
130
+ };
131
+ telegram?: {
132
+ telegramUserId: string;
133
+ firstName: string | null;
134
+ lastName: string | null;
135
+ username: string | null;
136
+ photoUrl: string | null;
137
+ };
138
+ /** MFA methods */
139
+ mfaMethods?: Array<string>;
140
+ /** Whether the user has accepted terms */
141
+ hasAcceptedTerms?: boolean;
142
+ /** Whether this is a guest user */
143
+ isGuest?: boolean;
144
+ }
145
+ /**
146
+ * Extracted profile properties from a Privy user.
147
+ * These are the properties that get sent as event properties via `identify()`.
148
+ */
149
+ export interface PrivyProfileProperties {
150
+ privyDid: string;
151
+ privyCreatedAt?: number;
152
+ email?: string;
153
+ apple?: string;
154
+ discord?: string;
155
+ twitter?: string;
156
+ farcaster?: string;
157
+ github?: string;
158
+ google?: string;
159
+ linkedin?: string;
160
+ line?: string;
161
+ spotify?: string;
162
+ telegram?: string;
163
+ tiktok?: string;
164
+ instagram?: string;
165
+ [key: string]: unknown;
166
+ }
167
+ /**
168
+ * Wallet info extracted from Privy linked accounts.
169
+ */
170
+ export interface PrivyWalletInfo {
171
+ address: string;
172
+ walletClient?: string;
173
+ chainType?: string;
174
+ isEmbedded: boolean;
175
+ }
176
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Privy-specific type definitions for user profile enrichment
3
+ *
4
+ * These types provide TypeScript interfaces for Privy user objects
5
+ * from the Privy React SDK (`usePrivy()` hook).
6
+ *
7
+ * Based on the Privy user object structure:
8
+ * https://docs.privy.io/user-management/users/the-user-object
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Utility functions for extracting profile properties from Privy user objects.
3
+ */
4
+ import { PrivyProfileProperties, PrivyUser, PrivyWalletInfo } from "./types";
5
+ /**
6
+ * Extract profile properties and wallet addresses from a Privy user object.
7
+ *
8
+ * Parses the Privy user's linked accounts into a flat properties object
9
+ * (email, social accounts, etc.) and extracts all linked wallet addresses.
10
+ *
11
+ * @param user - The Privy user object from `usePrivy()`
12
+ * @returns An object with `properties` and `wallets`
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { parsePrivyProperties } from '@formo/analytics';
17
+ *
18
+ * const { user } = usePrivy();
19
+ * if (user) {
20
+ * const { properties, wallets } = parsePrivyProperties(user);
21
+ *
22
+ * for (const wallet of wallets) {
23
+ * formo.identify({ address: wallet.address, userId: user.id }, properties);
24
+ * }
25
+ * }
26
+ * ```
27
+ */
28
+ export declare function parsePrivyProperties(user: PrivyUser): {
29
+ properties: PrivyProfileProperties;
30
+ wallets: PrivyWalletInfo[];
31
+ };
32
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Utility functions for extracting profile properties from Privy user objects.
3
+ */
4
+ /**
5
+ * Extract profile properties and wallet addresses from a Privy user object.
6
+ *
7
+ * Parses the Privy user's linked accounts into a flat properties object
8
+ * (email, social accounts, etc.) and extracts all linked wallet addresses.
9
+ *
10
+ * @param user - The Privy user object from `usePrivy()`
11
+ * @returns An object with `properties` and `wallets`
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { parsePrivyProperties } from '@formo/analytics';
16
+ *
17
+ * const { user } = usePrivy();
18
+ * if (user) {
19
+ * const { properties, wallets } = parsePrivyProperties(user);
20
+ *
21
+ * for (const wallet of wallets) {
22
+ * formo.identify({ address: wallet.address, userId: user.id }, properties);
23
+ * }
24
+ * }
25
+ * ```
26
+ */
27
+ export function parsePrivyProperties(user) {
28
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
29
+ var accounts = user.linkedAccounts || [];
30
+ // Extract profile properties
31
+ var properties = {
32
+ privyDid: user.id,
33
+ privyCreatedAt: (_a = user.createdAt) === null || _a === void 0 ? void 0 : _a.getTime(),
34
+ };
35
+ // Email
36
+ if ((_b = user.email) === null || _b === void 0 ? void 0 : _b.address) {
37
+ properties.email = user.email.address;
38
+ }
39
+ // Social accounts - extract usernames/identifiers
40
+ if ((_c = user.apple) === null || _c === void 0 ? void 0 : _c.email) {
41
+ properties.apple = user.apple.email;
42
+ }
43
+ if ((_d = user.discord) === null || _d === void 0 ? void 0 : _d.username) {
44
+ properties.discord = user.discord.username;
45
+ }
46
+ if ((_e = user.farcaster) === null || _e === void 0 ? void 0 : _e.username) {
47
+ properties.farcaster = user.farcaster.username;
48
+ }
49
+ if ((_f = user.github) === null || _f === void 0 ? void 0 : _f.username) {
50
+ properties.github = user.github.username;
51
+ }
52
+ if ((_g = user.google) === null || _g === void 0 ? void 0 : _g.email) {
53
+ properties.google = user.google.email;
54
+ }
55
+ if ((_h = user.instagram) === null || _h === void 0 ? void 0 : _h.username) {
56
+ properties.instagram = user.instagram.username;
57
+ }
58
+ if ((_j = user.line) === null || _j === void 0 ? void 0 : _j.email) {
59
+ properties.line = user.line.email;
60
+ }
61
+ if ((_k = user.linkedin) === null || _k === void 0 ? void 0 : _k.email) {
62
+ properties.linkedin = user.linkedin.email;
63
+ }
64
+ if ((_l = user.spotify) === null || _l === void 0 ? void 0 : _l.email) {
65
+ properties.spotify = user.spotify.email;
66
+ }
67
+ if ((_m = user.telegram) === null || _m === void 0 ? void 0 : _m.username) {
68
+ properties.telegram = user.telegram.username;
69
+ }
70
+ if ((_o = user.tiktok) === null || _o === void 0 ? void 0 : _o.username) {
71
+ properties.tiktok = user.tiktok.username;
72
+ }
73
+ if ((_p = user.twitter) === null || _p === void 0 ? void 0 : _p.username) {
74
+ properties.twitter = user.twitter.username;
75
+ }
76
+ // Fallback to linkedAccounts if convenience accessors are not populated
77
+ if (!properties.email) {
78
+ var emailAccount = accounts.find(function (account) { return account.type === "email"; });
79
+ if (emailAccount === null || emailAccount === void 0 ? void 0 : emailAccount.address) {
80
+ properties.email = emailAccount.address;
81
+ }
82
+ }
83
+ if (!properties.apple) {
84
+ var appleAccount = accounts.find(function (a) { return a.type === "apple_oauth"; });
85
+ if (appleAccount === null || appleAccount === void 0 ? void 0 : appleAccount.email) {
86
+ properties.apple = appleAccount.email;
87
+ }
88
+ }
89
+ if (!properties.discord) {
90
+ var discordAccount = accounts.find(function (a) { return a.type === "discord_oauth"; });
91
+ if (discordAccount === null || discordAccount === void 0 ? void 0 : discordAccount.username) {
92
+ properties.discord = discordAccount.username;
93
+ }
94
+ }
95
+ if (!properties.farcaster) {
96
+ var farcasterAccount = accounts.find(function (a) { return a.type === "farcaster"; });
97
+ if (farcasterAccount === null || farcasterAccount === void 0 ? void 0 : farcasterAccount.username) {
98
+ properties.farcaster = farcasterAccount.username;
99
+ }
100
+ else if (farcasterAccount === null || farcasterAccount === void 0 ? void 0 : farcasterAccount.displayName) {
101
+ properties.farcaster = farcasterAccount.displayName;
102
+ }
103
+ }
104
+ if (!properties.github) {
105
+ var githubAccount = accounts.find(function (a) { return a.type === "github_oauth"; });
106
+ if (githubAccount === null || githubAccount === void 0 ? void 0 : githubAccount.username) {
107
+ properties.github = githubAccount.username;
108
+ }
109
+ }
110
+ if (!properties.google) {
111
+ var googleAccount = accounts.find(function (a) { return a.type === "google_oauth"; });
112
+ if (googleAccount === null || googleAccount === void 0 ? void 0 : googleAccount.email) {
113
+ properties.google = googleAccount.email;
114
+ }
115
+ }
116
+ if (!properties.instagram) {
117
+ var instagramAccount = accounts.find(function (a) { return a.type === "instagram_oauth"; });
118
+ if (instagramAccount === null || instagramAccount === void 0 ? void 0 : instagramAccount.username) {
119
+ properties.instagram = instagramAccount.username;
120
+ }
121
+ }
122
+ if (!properties.line) {
123
+ var lineAccount = accounts.find(function (a) { return a.type === "line_oauth"; });
124
+ if (lineAccount === null || lineAccount === void 0 ? void 0 : lineAccount.email) {
125
+ properties.line = lineAccount.email;
126
+ }
127
+ }
128
+ if (!properties.linkedin) {
129
+ var linkedinAccount = accounts.find(function (a) { return a.type === "linkedin_oauth"; });
130
+ if (linkedinAccount === null || linkedinAccount === void 0 ? void 0 : linkedinAccount.email) {
131
+ properties.linkedin = linkedinAccount.email;
132
+ }
133
+ }
134
+ if (!properties.spotify) {
135
+ var spotifyAccount = accounts.find(function (a) { return a.type === "spotify_oauth"; });
136
+ if (spotifyAccount === null || spotifyAccount === void 0 ? void 0 : spotifyAccount.email) {
137
+ properties.spotify = spotifyAccount.email;
138
+ }
139
+ }
140
+ if (!properties.telegram) {
141
+ var telegramAccount = accounts.find(function (a) { return a.type === "telegram"; });
142
+ if (telegramAccount === null || telegramAccount === void 0 ? void 0 : telegramAccount.username) {
143
+ properties.telegram = telegramAccount.username;
144
+ }
145
+ else if (telegramAccount === null || telegramAccount === void 0 ? void 0 : telegramAccount.telegramUserId) {
146
+ properties.telegram = telegramAccount.telegramUserId;
147
+ }
148
+ }
149
+ if (!properties.tiktok) {
150
+ var tiktokAccount = accounts.find(function (a) { return a.type === "tiktok_oauth"; });
151
+ if (tiktokAccount === null || tiktokAccount === void 0 ? void 0 : tiktokAccount.username) {
152
+ properties.tiktok = tiktokAccount.username;
153
+ }
154
+ }
155
+ if (!properties.twitter) {
156
+ var twitterAccount = accounts.find(function (a) { return a.type === "twitter_oauth"; });
157
+ if (twitterAccount === null || twitterAccount === void 0 ? void 0 : twitterAccount.username) {
158
+ properties.twitter = twitterAccount.username;
159
+ }
160
+ }
161
+ // Use OAuth emails as fallback for email if still blank
162
+ // Priority: email -> google -> apple -> linkedin
163
+ if (!properties.email) {
164
+ if (properties.google) {
165
+ properties.email = properties.google;
166
+ }
167
+ else if (properties.apple) {
168
+ properties.email = properties.apple;
169
+ }
170
+ else if (properties.linkedin) {
171
+ properties.email = properties.linkedin;
172
+ }
173
+ }
174
+ // Extract wallet addresses
175
+ var wallets = accounts
176
+ .filter(function (a) { return (a.type === "wallet" || a.type === "smart_wallet") && a.address; })
177
+ .map(function (a) {
178
+ var _a, _b;
179
+ return ({
180
+ address: a.address,
181
+ walletClient: (_a = (a.walletClientType || a.walletClient)) !== null && _a !== void 0 ? _a : undefined,
182
+ chainType: (_b = a.chainType) !== null && _b !== void 0 ? _b : undefined,
183
+ isEmbedded: a.walletClientType === "privy" || a.walletClient === "privy",
184
+ });
185
+ });
186
+ return { properties: properties, wallets: wallets };
187
+ }
188
+ //# sourceMappingURL=utils.js.map
@@ -96,7 +96,8 @@ var FormoAnalyticsSession = /** @class */ (function () {
96
96
  var _a;
97
97
  var identifiedKey = this.generateIdentificationKey(address, rdns);
98
98
  var identifiedWallets = ((_a = cookie().get(SESSION_WALLET_IDENTIFIED_KEY)) === null || _a === void 0 ? void 0 : _a.split(",")) || [];
99
- if (!identifiedWallets.includes(identifiedKey)) {
99
+ var alreadyExists = identifiedWallets.includes(identifiedKey);
100
+ if (!alreadyExists) {
100
101
  identifiedWallets.push(identifiedKey);
101
102
  var newValue = identifiedWallets.join(",");
102
103
  cookie().set(SESSION_WALLET_IDENTIFIED_KEY, newValue, {