eggi-ai-db-schema-2 12.54.2

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 (188) hide show
  1. package/CHANGELOG.md +750 -0
  2. package/README.md +655 -0
  3. package/dist/config/database.d.ts +28 -0
  4. package/dist/config/database.d.ts.map +1 -0
  5. package/dist/config/database.js +72 -0
  6. package/dist/config/database.js.map +1 -0
  7. package/dist/index.d.ts +28 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +199 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/lib/database-service.d.ts +689 -0
  12. package/dist/lib/database-service.d.ts.map +1 -0
  13. package/dist/lib/database-service.js +1362 -0
  14. package/dist/lib/database-service.js.map +1 -0
  15. package/dist/lib/db-types.d.ts +167 -0
  16. package/dist/lib/db-types.d.ts.map +1 -0
  17. package/dist/lib/db-types.js +28 -0
  18. package/dist/lib/db-types.js.map +1 -0
  19. package/dist/lib/db.d.ts +58 -0
  20. package/dist/lib/db.d.ts.map +1 -0
  21. package/dist/lib/db.js +292 -0
  22. package/dist/lib/db.js.map +1 -0
  23. package/dist/lib/index.d.ts +11 -0
  24. package/dist/lib/index.d.ts.map +1 -0
  25. package/dist/lib/index.js +26 -0
  26. package/dist/lib/index.js.map +1 -0
  27. package/dist/lib/pg-client.d.ts +50 -0
  28. package/dist/lib/pg-client.d.ts.map +1 -0
  29. package/dist/lib/pg-client.js +106 -0
  30. package/dist/lib/pg-client.js.map +1 -0
  31. package/dist/lib/schema.d.ts +298 -0
  32. package/dist/lib/schema.d.ts.map +1 -0
  33. package/dist/lib/schema.js +12 -0
  34. package/dist/lib/schema.js.map +1 -0
  35. package/dist/migration-manager.d.ts +49 -0
  36. package/dist/migration-manager.d.ts.map +1 -0
  37. package/dist/migration-manager.js +282 -0
  38. package/dist/migration-manager.js.map +1 -0
  39. package/dist/queries/minimal-connections.d.ts +31 -0
  40. package/dist/queries/minimal-connections.d.ts.map +1 -0
  41. package/dist/queries/minimal-connections.js +143 -0
  42. package/dist/queries/minimal-connections.js.map +1 -0
  43. package/dist/schema.ts +340 -0
  44. package/dist/seed.d.ts +8 -0
  45. package/dist/seed.d.ts.map +1 -0
  46. package/dist/seed.js +40 -0
  47. package/dist/seed.js.map +1 -0
  48. package/dist/types/index.d.ts +7 -0
  49. package/dist/types/index.d.ts.map +1 -0
  50. package/dist/types/index.js +23 -0
  51. package/dist/types/index.js.map +1 -0
  52. package/dist/types/types.d.ts +77 -0
  53. package/dist/types/types.d.ts.map +1 -0
  54. package/dist/types/types.js +3 -0
  55. package/dist/types/types.js.map +1 -0
  56. package/dist/utils/authenticated-user-operations.d.ts +110 -0
  57. package/dist/utils/authenticated-user-operations.d.ts.map +1 -0
  58. package/dist/utils/authenticated-user-operations.js +292 -0
  59. package/dist/utils/authenticated-user-operations.js.map +1 -0
  60. package/dist/utils/authentication-operations.d.ts +48 -0
  61. package/dist/utils/authentication-operations.d.ts.map +1 -0
  62. package/dist/utils/authentication-operations.js +172 -0
  63. package/dist/utils/authentication-operations.js.map +1 -0
  64. package/dist/utils/company-mapping-job-operations.d.ts +103 -0
  65. package/dist/utils/company-mapping-job-operations.d.ts.map +1 -0
  66. package/dist/utils/company-mapping-job-operations.js +413 -0
  67. package/dist/utils/company-mapping-job-operations.js.map +1 -0
  68. package/dist/utils/company-sheet-upload-operations.d.ts +53 -0
  69. package/dist/utils/company-sheet-upload-operations.d.ts.map +1 -0
  70. package/dist/utils/company-sheet-upload-operations.js +135 -0
  71. package/dist/utils/company-sheet-upload-operations.js.map +1 -0
  72. package/dist/utils/contact-operations.d.ts +70 -0
  73. package/dist/utils/contact-operations.d.ts.map +1 -0
  74. package/dist/utils/contact-operations.js +294 -0
  75. package/dist/utils/contact-operations.js.map +1 -0
  76. package/dist/utils/forager-linkedin-operations.d.ts +74 -0
  77. package/dist/utils/forager-linkedin-operations.d.ts.map +1 -0
  78. package/dist/utils/forager-linkedin-operations.js +778 -0
  79. package/dist/utils/forager-linkedin-operations.js.map +1 -0
  80. package/dist/utils/ghost-genius-linkedin-operations.d.ts +23 -0
  81. package/dist/utils/ghost-genius-linkedin-operations.d.ts.map +1 -0
  82. package/dist/utils/ghost-genius-linkedin-operations.js +282 -0
  83. package/dist/utils/ghost-genius-linkedin-operations.js.map +1 -0
  84. package/dist/utils/index.d.ts +29 -0
  85. package/dist/utils/index.d.ts.map +1 -0
  86. package/dist/utils/index.js +77 -0
  87. package/dist/utils/index.js.map +1 -0
  88. package/dist/utils/introduction-request-operations.d.ts +159 -0
  89. package/dist/utils/introduction-request-operations.d.ts.map +1 -0
  90. package/dist/utils/introduction-request-operations.js +481 -0
  91. package/dist/utils/introduction-request-operations.js.map +1 -0
  92. package/dist/utils/invitation-operations.d.ts +141 -0
  93. package/dist/utils/invitation-operations.d.ts.map +1 -0
  94. package/dist/utils/invitation-operations.js +749 -0
  95. package/dist/utils/invitation-operations.js.map +1 -0
  96. package/dist/utils/linkedin-account-operations.d.ts +45 -0
  97. package/dist/utils/linkedin-account-operations.d.ts.map +1 -0
  98. package/dist/utils/linkedin-account-operations.js +279 -0
  99. package/dist/utils/linkedin-account-operations.js.map +1 -0
  100. package/dist/utils/linkedin-account-relationship-operations.d.ts +77 -0
  101. package/dist/utils/linkedin-account-relationship-operations.d.ts.map +1 -0
  102. package/dist/utils/linkedin-account-relationship-operations.js +274 -0
  103. package/dist/utils/linkedin-account-relationship-operations.js.map +1 -0
  104. package/dist/utils/linkedin-data-operations.d.ts +102 -0
  105. package/dist/utils/linkedin-data-operations.d.ts.map +1 -0
  106. package/dist/utils/linkedin-data-operations.js +613 -0
  107. package/dist/utils/linkedin-data-operations.js.map +1 -0
  108. package/dist/utils/linkedin-identifier-utils.d.ts +31 -0
  109. package/dist/utils/linkedin-identifier-utils.d.ts.map +1 -0
  110. package/dist/utils/linkedin-identifier-utils.js +63 -0
  111. package/dist/utils/linkedin-identifier-utils.js.map +1 -0
  112. package/dist/utils/linkedin-profile-cache.d.ts +131 -0
  113. package/dist/utils/linkedin-profile-cache.d.ts.map +1 -0
  114. package/dist/utils/linkedin-profile-cache.js +418 -0
  115. package/dist/utils/linkedin-profile-cache.js.map +1 -0
  116. package/dist/utils/llm-inference-job-operations.d.ts +116 -0
  117. package/dist/utils/llm-inference-job-operations.d.ts.map +1 -0
  118. package/dist/utils/llm-inference-job-operations.js +266 -0
  119. package/dist/utils/llm-inference-job-operations.js.map +1 -0
  120. package/dist/utils/mapping-job-operations.d.ts +272 -0
  121. package/dist/utils/mapping-job-operations.d.ts.map +1 -0
  122. package/dist/utils/mapping-job-operations.js +833 -0
  123. package/dist/utils/mapping-job-operations.js.map +1 -0
  124. package/dist/utils/mapping-operations.d.ts +80 -0
  125. package/dist/utils/mapping-operations.d.ts.map +1 -0
  126. package/dist/utils/mapping-operations.js +318 -0
  127. package/dist/utils/mapping-operations.js.map +1 -0
  128. package/dist/utils/on-demand-mapping-operations.d.ts +199 -0
  129. package/dist/utils/on-demand-mapping-operations.d.ts.map +1 -0
  130. package/dist/utils/on-demand-mapping-operations.js +728 -0
  131. package/dist/utils/on-demand-mapping-operations.js.map +1 -0
  132. package/dist/utils/onboarding-operations.d.ts +53 -0
  133. package/dist/utils/onboarding-operations.d.ts.map +1 -0
  134. package/dist/utils/onboarding-operations.js +223 -0
  135. package/dist/utils/onboarding-operations.js.map +1 -0
  136. package/dist/utils/organization-assignment-job-operations.d.ts +258 -0
  137. package/dist/utils/organization-assignment-job-operations.d.ts.map +1 -0
  138. package/dist/utils/organization-assignment-job-operations.js +881 -0
  139. package/dist/utils/organization-assignment-job-operations.js.map +1 -0
  140. package/dist/utils/organization-assignment-operations.d.ts +59 -0
  141. package/dist/utils/organization-assignment-operations.d.ts.map +1 -0
  142. package/dist/utils/organization-assignment-operations.js +130 -0
  143. package/dist/utils/organization-assignment-operations.js.map +1 -0
  144. package/dist/utils/organization-operations.d.ts +275 -0
  145. package/dist/utils/organization-operations.d.ts.map +1 -0
  146. package/dist/utils/organization-operations.js +993 -0
  147. package/dist/utils/organization-operations.js.map +1 -0
  148. package/dist/utils/organization-relationship-operations.d.ts +59 -0
  149. package/dist/utils/organization-relationship-operations.d.ts.map +1 -0
  150. package/dist/utils/organization-relationship-operations.js +240 -0
  151. package/dist/utils/organization-relationship-operations.js.map +1 -0
  152. package/dist/utils/quota-operations.d.ts +107 -0
  153. package/dist/utils/quota-operations.d.ts.map +1 -0
  154. package/dist/utils/quota-operations.js +692 -0
  155. package/dist/utils/quota-operations.js.map +1 -0
  156. package/dist/utils/recursive-mapping-job-operations.d.ts +42 -0
  157. package/dist/utils/recursive-mapping-job-operations.d.ts.map +1 -0
  158. package/dist/utils/recursive-mapping-job-operations.js +169 -0
  159. package/dist/utils/recursive-mapping-job-operations.js.map +1 -0
  160. package/dist/utils/relationship-operations.d.ts +130 -0
  161. package/dist/utils/relationship-operations.d.ts.map +1 -0
  162. package/dist/utils/relationship-operations.js +329 -0
  163. package/dist/utils/relationship-operations.js.map +1 -0
  164. package/dist/utils/sales-pipeline-operations.d.ts +143 -0
  165. package/dist/utils/sales-pipeline-operations.d.ts.map +1 -0
  166. package/dist/utils/sales-pipeline-operations.js +649 -0
  167. package/dist/utils/sales-pipeline-operations.js.map +1 -0
  168. package/dist/utils/skills-operations.d.ts +117 -0
  169. package/dist/utils/skills-operations.d.ts.map +1 -0
  170. package/dist/utils/skills-operations.js +487 -0
  171. package/dist/utils/skills-operations.js.map +1 -0
  172. package/dist/utils/subscription-operations.d.ts +123 -0
  173. package/dist/utils/subscription-operations.d.ts.map +1 -0
  174. package/dist/utils/subscription-operations.js +391 -0
  175. package/dist/utils/subscription-operations.js.map +1 -0
  176. package/dist/utils/unipile-account-operations.d.ts +96 -0
  177. package/dist/utils/unipile-account-operations.d.ts.map +1 -0
  178. package/dist/utils/unipile-account-operations.js +255 -0
  179. package/dist/utils/unipile-account-operations.js.map +1 -0
  180. package/dist/utils/user-industry-operations.d.ts +80 -0
  181. package/dist/utils/user-industry-operations.d.ts.map +1 -0
  182. package/dist/utils/user-industry-operations.js +237 -0
  183. package/dist/utils/user-industry-operations.js.map +1 -0
  184. package/dist/utils/user-operations.d.ts +87 -0
  185. package/dist/utils/user-operations.d.ts.map +1 -0
  186. package/dist/utils/user-operations.js +212 -0
  187. package/dist/utils/user-operations.js.map +1 -0
  188. package/package.json +98 -0
@@ -0,0 +1,778 @@
1
+ "use strict";
2
+ /**
3
+ * Forager-based LinkedIn Account Operations
4
+ *
5
+ * Migrated to native PostgreSQL queries for better performance and flexibility.
6
+ * All functions maintain the same interface for backward compatibility.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.findLinkedInAccountByForagerId = findLinkedInAccountByForagerId;
10
+ exports.findLinkedInAccountByPublicIdentifier = findLinkedInAccountByPublicIdentifier;
11
+ exports.findLinkedInAccountByAnyIdentifier = findLinkedInAccountByAnyIdentifier;
12
+ exports.findOrCreateLinkedInAccountByForagerId = findOrCreateLinkedInAccountByForagerId;
13
+ exports.findLinkedInAccountWithUserByForagerId = findLinkedInAccountWithUserByForagerId;
14
+ exports.updateLinkedInAccountProfileByForagerId = updateLinkedInAccountProfileByForagerId;
15
+ exports.updateLinkedInAccountAcoaIdentifierByForagerId = updateLinkedInAccountAcoaIdentifierByForagerId;
16
+ exports.updateLinkedInAccountAcoaIdentifierByForagerIdWithChangeDetection = updateLinkedInAccountAcoaIdentifierByForagerIdWithChangeDetection;
17
+ const pg_client_1 = require("../lib/pg-client");
18
+ const db_1 = require("../lib/db");
19
+ /**
20
+ * Maps database row (snake_case) to User interface (camelCase)
21
+ */
22
+ function mapToUser(row) {
23
+ return {
24
+ id: row.id,
25
+ givenName: row.given_name,
26
+ familyName: row.family_name,
27
+ summary: row.summary,
28
+ embeddingSummary: row.embedding_summary,
29
+ embedding: row.embedding,
30
+ createdAt: row.created_at,
31
+ };
32
+ }
33
+ // Simple logger for this module
34
+ const logger = {
35
+ info: (message, data) => console.log(`[INFO] ${message}`, data ? JSON.stringify(data, null, 2) : ""),
36
+ error: (message, data) => console.error(`[ERROR] ${message}`, data ? JSON.stringify(data, null, 2) : ""),
37
+ };
38
+ // =============================================================================
39
+ // HELPER FUNCTIONS
40
+ // =============================================================================
41
+ /**
42
+ * Convert database row to LinkedInAccountResult with computed profilePictureUrl
43
+ */
44
+ function mapToLinkedInAccountResult(row) {
45
+ return {
46
+ id: row.id,
47
+ user_id: row.user_id,
48
+ forager_id: row.forager_id,
49
+ public_identifier: row.public_identifier,
50
+ linkedin_identifier_acoa: row.linkedin_identifier_acoa,
51
+ first_name: row.first_name,
52
+ last_name: row.last_name,
53
+ headline: row.headline,
54
+ summary: row.summary,
55
+ location: row.location,
56
+ profile_picture_url: row.profile_picture_url, // Already computed via COALESCE in SQL
57
+ profile_image_cloudfront_url: null, // Always hidden
58
+ profile_image_hash: row.profile_image_hash || null,
59
+ follower_count: row.follower_count,
60
+ connections_count: row.connections_count,
61
+ profiles_updated_at: row.profiles_updated_at,
62
+ created_at: row.created_at,
63
+ };
64
+ }
65
+ // =============================================================================
66
+ // FORAGER-BASED LINKEDIN ACCOUNT OPERATIONS
67
+ // =============================================================================
68
+ /**
69
+ * Find LinkedIn account by Forager ID (primary matching method)
70
+ */
71
+ async function findLinkedInAccountByForagerId(db, foragerId) {
72
+ (0, db_1.debugLogDbOperation)("select", "linkedin.accounts", { foragerId }, undefined, {
73
+ operation: "findLinkedInAccountByForagerId",
74
+ foragerId,
75
+ });
76
+ try {
77
+ const sql = `
78
+ SELECT
79
+ id,
80
+ user_id,
81
+ forager_id,
82
+ public_identifier,
83
+ linkedin_identifier_acoa,
84
+ first_name,
85
+ last_name,
86
+ headline,
87
+ summary,
88
+ location,
89
+ COALESCE(profile_image_cloudfront_url, profile_picture_url) AS profile_picture_url,
90
+ follower_count,
91
+ connections_count,
92
+ profiles_updated_at,
93
+ created_at
94
+ FROM linkedin.accounts
95
+ WHERE forager_id = $1
96
+ LIMIT 1
97
+ `;
98
+ const result = await (0, pg_client_1.queryOne)(db, sql, [foragerId]);
99
+ return result ? mapToLinkedInAccountResult(result) : null;
100
+ }
101
+ catch (error) {
102
+ console.error("Error finding LinkedIn account by Forager ID:", error);
103
+ throw error;
104
+ }
105
+ }
106
+ /**
107
+ * Find LinkedIn account by public identifier (fallback method)
108
+ */
109
+ async function findLinkedInAccountByPublicIdentifier(db, publicIdentifier) {
110
+ (0, db_1.debugLogDbOperation)("select", "linkedin.accounts", { publicIdentifier }, undefined, {
111
+ operation: "findLinkedInAccountByPublicIdentifier",
112
+ publicIdentifier,
113
+ });
114
+ try {
115
+ const sql = `
116
+ SELECT
117
+ id,
118
+ user_id,
119
+ forager_id,
120
+ public_identifier,
121
+ linkedin_identifier_acoa,
122
+ first_name,
123
+ last_name,
124
+ headline,
125
+ summary,
126
+ location,
127
+ COALESCE(profile_image_cloudfront_url, profile_picture_url) AS profile_picture_url,
128
+ follower_count,
129
+ connections_count,
130
+ profiles_updated_at,
131
+ created_at
132
+ FROM linkedin.accounts
133
+ WHERE public_identifier = $1
134
+ LIMIT 1
135
+ `;
136
+ const result = await (0, pg_client_1.queryOne)(db, sql, [publicIdentifier]);
137
+ return result ? mapToLinkedInAccountResult(result) : null;
138
+ }
139
+ catch (error) {
140
+ console.error("Error finding LinkedIn account by public identifier:", error);
141
+ throw error;
142
+ }
143
+ }
144
+ /**
145
+ * Find LinkedIn account by any identifier (Forager ID, public identifier, or ACoA)
146
+ * Priority: forager_id > public_identifier > linkedin_identifier_acoa
147
+ */
148
+ async function findLinkedInAccountByAnyIdentifier(db, identifier) {
149
+ (0, db_1.debugLogDbOperation)("select", "linkedin.accounts", { identifier }, undefined, {
150
+ operation: "findLinkedInAccountByAnyIdentifier",
151
+ identifier,
152
+ });
153
+ try {
154
+ // Try Forager ID first if it's a number
155
+ if (typeof identifier === "number") {
156
+ const foragerResult = await findLinkedInAccountByForagerId(db, identifier);
157
+ if (foragerResult)
158
+ return foragerResult;
159
+ }
160
+ // Try public identifier
161
+ if (typeof identifier === "string") {
162
+ const publicResult = await findLinkedInAccountByPublicIdentifier(db, identifier);
163
+ if (publicResult)
164
+ return publicResult;
165
+ }
166
+ // Try ACoA identifier as last resort
167
+ if (typeof identifier === "string" && identifier.startsWith("ACoA")) {
168
+ const sql = `
169
+ SELECT
170
+ id,
171
+ user_id,
172
+ forager_id,
173
+ public_identifier,
174
+ linkedin_identifier_acoa,
175
+ first_name,
176
+ last_name,
177
+ headline,
178
+ summary,
179
+ location,
180
+ COALESCE(profile_image_cloudfront_url, profile_picture_url) AS profile_picture_url,
181
+ follower_count,
182
+ connections_count,
183
+ profiles_updated_at,
184
+ created_at
185
+ FROM linkedin.accounts
186
+ WHERE linkedin_identifier_acoa = $1
187
+ LIMIT 1
188
+ `;
189
+ const result = await (0, pg_client_1.queryOne)(db, sql, [identifier]);
190
+ return result ? mapToLinkedInAccountResult(result) : null;
191
+ }
192
+ return null;
193
+ }
194
+ catch (error) {
195
+ console.error("Error finding LinkedIn account by any identifier:", error);
196
+ throw error;
197
+ }
198
+ }
199
+ /**
200
+ * Find or create LinkedIn account by Forager ID
201
+ */
202
+ async function findOrCreateLinkedInAccountByForagerId(db, foragerId, publicIdentifier, linkedinIdentifierAcoa, userId) {
203
+ (0, db_1.debugLogDbOperation)("upsert", "linkedin.accounts", {
204
+ foragerId,
205
+ publicIdentifier,
206
+ linkedinIdentifierAcoa,
207
+ userId,
208
+ }, undefined, {
209
+ operation: "findOrCreateLinkedInAccountByForagerId",
210
+ foragerId,
211
+ });
212
+ try {
213
+ // First, check if a record already exists with the same foragerId
214
+ const existingByForagerIdSql = `
215
+ SELECT id, forager_id
216
+ FROM linkedin.accounts
217
+ WHERE forager_id = $1
218
+ LIMIT 1
219
+ `;
220
+ const existingByForagerId = await (0, pg_client_1.queryOne)(db, existingByForagerIdSql, [foragerId]);
221
+ if (existingByForagerId) {
222
+ // Record already exists - update it
223
+ // First, check current values to determine if update will change data
224
+ const currentValuesSql = `
225
+ SELECT
226
+ public_identifier,
227
+ linkedin_identifier_acoa,
228
+ user_id
229
+ FROM linkedin.accounts
230
+ WHERE id = $1
231
+ `;
232
+ const currentValues = await (0, pg_client_1.queryOne)(db, currentValuesSql, [existingByForagerId.id]);
233
+ if (!currentValues) {
234
+ throw new Error(`LinkedIn account not found for id: ${existingByForagerId.id}`);
235
+ }
236
+ // Determine if update will actually change data
237
+ const willUpdatePublicIdentifier = publicIdentifier !== null &&
238
+ publicIdentifier !== undefined &&
239
+ currentValues.public_identifier !== publicIdentifier;
240
+ const willUpdateAcoa = linkedinIdentifierAcoa !== null &&
241
+ linkedinIdentifierAcoa !== undefined &&
242
+ currentValues.linkedin_identifier_acoa !== linkedinIdentifierAcoa;
243
+ const willUpdateUserId = userId !== undefined && userId !== null && currentValues.user_id !== userId;
244
+ const willUpdate = willUpdatePublicIdentifier || willUpdateAcoa || willUpdateUserId;
245
+ // Log the decision-making process for tracking
246
+ logger.info("LinkedIn account update decision", {
247
+ foragerId,
248
+ accountId: existingByForagerId.id,
249
+ willUpdate,
250
+ willUpdatePublicIdentifier,
251
+ willUpdateAcoa,
252
+ willUpdateUserId,
253
+ currentPublicIdentifier: currentValues.public_identifier,
254
+ newPublicIdentifier: publicIdentifier,
255
+ currentAcoa: currentValues.linkedin_identifier_acoa,
256
+ newAcoa: linkedinIdentifierAcoa,
257
+ currentUserId: currentValues.user_id,
258
+ newUserId: userId,
259
+ });
260
+ // Perform update if there are changes
261
+ if (willUpdate) {
262
+ const updateSql = `
263
+ UPDATE linkedin.accounts
264
+ SET
265
+ public_identifier = COALESCE($2, public_identifier),
266
+ linkedin_identifier_acoa = COALESCE($3, linkedin_identifier_acoa)
267
+ ${userId !== undefined ? ", user_id = $4" : ""}
268
+ WHERE id = $1
269
+ RETURNING id
270
+ `;
271
+ const updateParams = userId !== undefined
272
+ ? [existingByForagerId.id, publicIdentifier, linkedinIdentifierAcoa, userId]
273
+ : [existingByForagerId.id, publicIdentifier, linkedinIdentifierAcoa];
274
+ await (0, pg_client_1.queryOne)(db, updateSql, updateParams);
275
+ logger.info("LinkedIn account updated successfully", {
276
+ foragerId,
277
+ accountId: existingByForagerId.id,
278
+ wasUpdated: true,
279
+ });
280
+ }
281
+ else {
282
+ logger.info("LinkedIn account exists but no changes needed", {
283
+ foragerId,
284
+ accountId: existingByForagerId.id,
285
+ wasUpdated: false,
286
+ });
287
+ }
288
+ return {
289
+ id: existingByForagerId.id,
290
+ wasNewlyCreated: false,
291
+ wasUpdated: willUpdate,
292
+ };
293
+ }
294
+ // Check if a record exists with the same ACoA identifier (different foragerId)
295
+ if (linkedinIdentifierAcoa) {
296
+ const existingByAcoaSql = `
297
+ SELECT id, forager_id
298
+ FROM linkedin.accounts
299
+ WHERE linkedin_identifier_acoa = $1
300
+ LIMIT 1
301
+ `;
302
+ const existingByAcoa = await (0, pg_client_1.queryOne)(db, existingByAcoaSql, [linkedinIdentifierAcoa]);
303
+ if (existingByAcoa) {
304
+ // Update existing record with new foragerId
305
+ // First, check current values to determine if update will change data
306
+ const currentValuesSql = `
307
+ SELECT
308
+ forager_id,
309
+ public_identifier,
310
+ user_id
311
+ FROM linkedin.accounts
312
+ WHERE id = $1
313
+ `;
314
+ const currentValues = await (0, pg_client_1.queryOne)(db, currentValuesSql, [existingByAcoa.id]);
315
+ if (!currentValues) {
316
+ throw new Error(`LinkedIn account not found for id: ${existingByAcoa.id}`);
317
+ }
318
+ // Determine if update will actually change data
319
+ const willUpdateForagerId = currentValues.forager_id !== foragerId;
320
+ const willUpdatePublicIdentifier = publicIdentifier !== null &&
321
+ publicIdentifier !== undefined &&
322
+ currentValues.public_identifier !== publicIdentifier;
323
+ const willUpdateUserId = userId !== undefined && userId !== null && currentValues.user_id !== userId;
324
+ const willUpdate = willUpdateForagerId || willUpdatePublicIdentifier || willUpdateUserId;
325
+ // Log the decision-making process for tracking (ACoA match)
326
+ logger.info("LinkedIn account update decision (ACoA match)", {
327
+ foragerId,
328
+ accountId: existingByAcoa.id,
329
+ willUpdate,
330
+ willUpdateForagerId,
331
+ willUpdatePublicIdentifier,
332
+ willUpdateUserId,
333
+ currentForagerId: currentValues.forager_id,
334
+ newForagerId: foragerId,
335
+ currentPublicIdentifier: currentValues.public_identifier,
336
+ newPublicIdentifier: publicIdentifier,
337
+ currentUserId: currentValues.user_id,
338
+ newUserId: userId,
339
+ });
340
+ // Perform update if there are changes
341
+ if (willUpdate) {
342
+ const updateSql = `
343
+ UPDATE linkedin.accounts
344
+ SET
345
+ forager_id = $2,
346
+ public_identifier = COALESCE($3, public_identifier)
347
+ ${userId !== undefined ? ", user_id = $4" : ""}
348
+ WHERE id = $1
349
+ RETURNING id
350
+ `;
351
+ const updateParams = userId !== undefined
352
+ ? [existingByAcoa.id, foragerId, publicIdentifier, userId]
353
+ : [existingByAcoa.id, foragerId, publicIdentifier];
354
+ await (0, pg_client_1.queryOne)(db, updateSql, updateParams);
355
+ logger.info("LinkedIn account updated successfully (ACoA match)", {
356
+ foragerId,
357
+ accountId: existingByAcoa.id,
358
+ wasUpdated: true,
359
+ });
360
+ }
361
+ else {
362
+ logger.info("LinkedIn account exists but no changes needed (ACoA match)", {
363
+ foragerId,
364
+ accountId: existingByAcoa.id,
365
+ wasUpdated: false,
366
+ });
367
+ }
368
+ return {
369
+ id: existingByAcoa.id,
370
+ wasNewlyCreated: false,
371
+ wasUpdated: willUpdate,
372
+ };
373
+ }
374
+ }
375
+ // No existing record found - create new one
376
+ const insertSql = `
377
+ INSERT INTO linkedin.accounts (
378
+ forager_id,
379
+ public_identifier,
380
+ linkedin_identifier_acoa,
381
+ user_id,
382
+ created_at
383
+ ) VALUES ($1, $2, $3, $4, NOW())
384
+ RETURNING id
385
+ `;
386
+ const result = await (0, pg_client_1.queryOne)(db, insertSql, [
387
+ foragerId,
388
+ publicIdentifier || null,
389
+ linkedinIdentifierAcoa || null,
390
+ userId || null,
391
+ ]);
392
+ if (!result) {
393
+ throw new Error("Failed to create LinkedIn account");
394
+ }
395
+ logger.info("LinkedIn account created successfully", {
396
+ foragerId,
397
+ accountId: result.id,
398
+ publicIdentifier,
399
+ linkedinIdentifierAcoa,
400
+ userId,
401
+ wasNewlyCreated: true,
402
+ wasUpdated: false,
403
+ });
404
+ return {
405
+ id: result.id,
406
+ wasNewlyCreated: true,
407
+ wasUpdated: false, // New record, not an update
408
+ };
409
+ }
410
+ catch (error) {
411
+ // If it's a duplicate key constraint violation on linkedin_identifier_acoa,
412
+ // try to find the existing record instead of failing
413
+ if (error instanceof Error &&
414
+ error.message.includes('duplicate key value violates unique constraint "accounts_linkedin_identifier_acoa_unique"')) {
415
+ logger.info("Found existing LinkedIn account with same ACoA identifier, updating it", {
416
+ foragerId,
417
+ linkedinIdentifierAcoa,
418
+ publicIdentifier,
419
+ userId,
420
+ });
421
+ // Find the existing record by ACoA identifier
422
+ const existingByAcoaSql = `
423
+ SELECT id, forager_id
424
+ FROM linkedin.accounts
425
+ WHERE linkedin_identifier_acoa = $1
426
+ LIMIT 1
427
+ `;
428
+ const existingByAcoa = await (0, pg_client_1.queryOne)(db, existingByAcoaSql, [linkedinIdentifierAcoa]);
429
+ if (existingByAcoa) {
430
+ // Update the existing record with new foragerId and userId if provided
431
+ // First, check current values to determine if update will change data
432
+ const currentValuesSql = `
433
+ SELECT
434
+ forager_id,
435
+ public_identifier,
436
+ user_id
437
+ FROM linkedin.accounts
438
+ WHERE id = $1
439
+ `;
440
+ const currentValues = await (0, pg_client_1.queryOne)(db, currentValuesSql, [existingByAcoa.id]);
441
+ if (!currentValues) {
442
+ throw new Error(`LinkedIn account not found for id: ${existingByAcoa.id}`);
443
+ }
444
+ // Determine if update will actually change data
445
+ const willUpdateForagerId = currentValues.forager_id !== foragerId;
446
+ const willUpdatePublicIdentifier = publicIdentifier !== null &&
447
+ publicIdentifier !== undefined &&
448
+ currentValues.public_identifier !== publicIdentifier;
449
+ const willUpdateUserId = userId !== undefined && userId !== null && currentValues.user_id !== userId;
450
+ const willUpdate = willUpdateForagerId || willUpdatePublicIdentifier || willUpdateUserId;
451
+ // Log the decision-making process for tracking (duplicate key recovery)
452
+ logger.info("LinkedIn account update decision (duplicate key recovery)", {
453
+ foragerId,
454
+ accountId: existingByAcoa.id,
455
+ willUpdate,
456
+ willUpdateForagerId,
457
+ willUpdatePublicIdentifier,
458
+ willUpdateUserId,
459
+ currentForagerId: currentValues.forager_id,
460
+ newForagerId: foragerId,
461
+ currentPublicIdentifier: currentValues.public_identifier,
462
+ newPublicIdentifier: publicIdentifier,
463
+ currentUserId: currentValues.user_id,
464
+ newUserId: userId,
465
+ });
466
+ // Perform update if there are changes
467
+ if (willUpdate) {
468
+ const updateSql = `
469
+ UPDATE linkedin.accounts
470
+ SET
471
+ forager_id = $2,
472
+ public_identifier = COALESCE($3, public_identifier)
473
+ ${userId !== undefined ? ", user_id = $4" : ""}
474
+ WHERE id = $1
475
+ RETURNING id
476
+ `;
477
+ const updateParams = userId !== undefined
478
+ ? [existingByAcoa.id, foragerId, publicIdentifier, userId]
479
+ : [existingByAcoa.id, foragerId, publicIdentifier];
480
+ await (0, pg_client_1.queryOne)(db, updateSql, updateParams);
481
+ logger.info("LinkedIn account updated successfully (duplicate key recovery)", {
482
+ foragerId,
483
+ accountId: existingByAcoa.id,
484
+ wasUpdated: true,
485
+ });
486
+ }
487
+ else {
488
+ logger.info("LinkedIn account exists but no changes needed (duplicate key recovery)", {
489
+ foragerId,
490
+ accountId: existingByAcoa.id,
491
+ wasUpdated: false,
492
+ });
493
+ }
494
+ return {
495
+ id: existingByAcoa.id,
496
+ wasNewlyCreated: false,
497
+ wasUpdated: willUpdate,
498
+ };
499
+ }
500
+ // If existingByAcoa is null, fall through to error handling
501
+ }
502
+ console.error("Error in findOrCreateLinkedInAccountByForagerId:", error);
503
+ throw error;
504
+ }
505
+ }
506
+ /**
507
+ * Find LinkedIn account with associated user by Forager ID
508
+ */
509
+ async function findLinkedInAccountWithUserByForagerId(db, foragerId) {
510
+ (0, db_1.debugLogDbOperation)("select", "linkedin.accounts + users", { foragerId }, undefined, {
511
+ operation: "findLinkedInAccountWithUserByForagerId",
512
+ foragerId,
513
+ });
514
+ try {
515
+ const sql = `
516
+ SELECT
517
+ la.id,
518
+ la.user_id,
519
+ la.forager_id,
520
+ la.public_identifier,
521
+ la.linkedin_identifier_acoa,
522
+ la.first_name,
523
+ la.last_name,
524
+ la.headline,
525
+ la.summary,
526
+ la.location,
527
+ COALESCE(la.profile_image_cloudfront_url, la.profile_picture_url) AS profile_picture_url,
528
+ la.follower_count,
529
+ la.connections_count,
530
+ la.profiles_updated_at,
531
+ la.created_at,
532
+ u.id AS user_id_joined,
533
+ u.given_name AS user_given_name,
534
+ u.family_name AS user_family_name,
535
+ u.created_at AS user_created_at
536
+ FROM linkedin.accounts la
537
+ LEFT JOIN public.users u ON la.user_id = u.id
538
+ WHERE la.forager_id = $1
539
+ LIMIT 1
540
+ `;
541
+ const result = await (0, pg_client_1.queryOne)(db, sql, [foragerId]);
542
+ if (!result) {
543
+ return null;
544
+ }
545
+ return {
546
+ account: mapToLinkedInAccountResult(result),
547
+ user: result.user_id_joined
548
+ ? mapToUser({
549
+ id: result.user_id_joined,
550
+ given_name: result.user_given_name,
551
+ family_name: result.user_family_name,
552
+ summary: null,
553
+ embedding_summary: null,
554
+ embedding: null,
555
+ created_at: result.user_created_at,
556
+ })
557
+ : null,
558
+ };
559
+ }
560
+ catch (error) {
561
+ console.error("Error finding LinkedIn account with user by Forager ID:", error);
562
+ throw error;
563
+ }
564
+ }
565
+ /**
566
+ * Update LinkedIn account profile data by Forager ID
567
+ */
568
+ async function updateLinkedInAccountProfileByForagerId(db, foragerId, profileData) {
569
+ (0, db_1.debugLogDbOperation)("update", "linkedin.accounts", { foragerId, ...profileData }, undefined, {
570
+ operation: "updateLinkedInAccountProfileByForagerId",
571
+ foragerId,
572
+ });
573
+ try {
574
+ // Build dynamic SET clause
575
+ const updates = [];
576
+ const params = [foragerId];
577
+ let paramIndex = 2;
578
+ if (profileData.firstName !== undefined) {
579
+ updates.push(`first_name = $${paramIndex++}`);
580
+ params.push(profileData.firstName);
581
+ }
582
+ if (profileData.lastName !== undefined) {
583
+ updates.push(`last_name = $${paramIndex++}`);
584
+ params.push(profileData.lastName);
585
+ }
586
+ if (profileData.headline !== undefined) {
587
+ updates.push(`headline = $${paramIndex++}`);
588
+ params.push(profileData.headline);
589
+ }
590
+ if (profileData.summary !== undefined) {
591
+ updates.push(`summary = $${paramIndex++}`);
592
+ params.push(profileData.summary);
593
+ }
594
+ if (profileData.location !== undefined) {
595
+ updates.push(`location = $${paramIndex++}`);
596
+ params.push(profileData.location);
597
+ }
598
+ if (profileData.profilePictureUrl !== undefined) {
599
+ updates.push(`profile_picture_url = $${paramIndex++}`);
600
+ params.push(profileData.profilePictureUrl);
601
+ }
602
+ if (profileData.followerCount !== undefined) {
603
+ updates.push(`follower_count = $${paramIndex++}`);
604
+ params.push(profileData.followerCount);
605
+ }
606
+ if (profileData.connectionsCount !== undefined) {
607
+ updates.push(`connections_count = $${paramIndex++}`);
608
+ params.push(profileData.connectionsCount);
609
+ }
610
+ // Always update profiles_updated_at
611
+ updates.push(`profiles_updated_at = NOW()`);
612
+ const sql = `
613
+ UPDATE linkedin.accounts
614
+ SET ${updates.join(", ")}
615
+ WHERE forager_id = $1
616
+ RETURNING
617
+ id,
618
+ user_id,
619
+ forager_id,
620
+ public_identifier,
621
+ linkedin_identifier_acoa,
622
+ first_name,
623
+ last_name,
624
+ headline,
625
+ summary,
626
+ location,
627
+ COALESCE(profile_image_cloudfront_url, profile_picture_url) AS profile_picture_url,
628
+ follower_count,
629
+ connections_count,
630
+ profiles_updated_at,
631
+ created_at
632
+ `;
633
+ const result = await (0, pg_client_1.queryOne)(db, sql, params);
634
+ if (!result) {
635
+ throw new Error(`LinkedIn account with Forager ID ${foragerId} not found`);
636
+ }
637
+ return mapToLinkedInAccountResult(result);
638
+ }
639
+ catch (error) {
640
+ console.error("Error updating LinkedIn account profile by Forager ID:", error);
641
+ throw error;
642
+ }
643
+ }
644
+ /**
645
+ * Update LinkedIn account ACoA identifier by Forager ID
646
+ */
647
+ async function updateLinkedInAccountAcoaIdentifierByForagerId(db, foragerId, linkedinIdentifierAcoa) {
648
+ (0, db_1.debugLogDbOperation)("update", "linkedin.accounts", { foragerId, linkedinIdentifierAcoa }, undefined, {
649
+ operation: "updateLinkedInAccountAcoaIdentifierByForagerId",
650
+ foragerId,
651
+ });
652
+ try {
653
+ const sql = `
654
+ UPDATE linkedin.accounts
655
+ SET
656
+ linkedin_identifier_acoa = $2,
657
+ profiles_updated_at = NOW()
658
+ WHERE forager_id = $1
659
+ RETURNING
660
+ id,
661
+ user_id,
662
+ forager_id,
663
+ public_identifier,
664
+ linkedin_identifier_acoa,
665
+ first_name,
666
+ last_name,
667
+ headline,
668
+ summary,
669
+ location,
670
+ profile_picture_url,
671
+ profile_image_cloudfront_url,
672
+ follower_count,
673
+ connections_count,
674
+ profiles_updated_at,
675
+ created_at
676
+ `;
677
+ const result = await (0, pg_client_1.queryOne)(db, sql, [foragerId, linkedinIdentifierAcoa]);
678
+ if (!result) {
679
+ throw new Error(`LinkedIn account with Forager ID ${foragerId} not found`);
680
+ }
681
+ return {
682
+ ...mapToLinkedInAccountResult(result),
683
+ profile_image_cloudfront_url: null,
684
+ };
685
+ }
686
+ catch (error) {
687
+ console.error("Error updating LinkedIn account ACoA identifier by Forager ID:", error);
688
+ throw error;
689
+ }
690
+ }
691
+ /**
692
+ * Update LinkedIn account ACoA identifier by Forager ID with change detection
693
+ * Returns whether the value actually changed
694
+ */
695
+ async function updateLinkedInAccountAcoaIdentifierByForagerIdWithChangeDetection(db, foragerId, linkedinIdentifierAcoa) {
696
+ (0, db_1.debugLogDbOperation)("update", "linkedin.accounts", { foragerId, linkedinIdentifierAcoa }, undefined, {
697
+ operation: "updateLinkedInAccountAcoaIdentifierByForagerIdWithChangeDetection",
698
+ foragerId,
699
+ });
700
+ try {
701
+ // Use a single query with RETURNING to get both old and new values
702
+ const sql = `
703
+ UPDATE linkedin.accounts
704
+ SET
705
+ linkedin_identifier_acoa = $2,
706
+ profiles_updated_at = NOW()
707
+ WHERE forager_id = $1
708
+ RETURNING
709
+ id,
710
+ user_id,
711
+ forager_id,
712
+ public_identifier,
713
+ linkedin_identifier_acoa AS new_acoa,
714
+ (SELECT linkedin_identifier_acoa FROM linkedin.accounts WHERE forager_id = $1 FOR UPDATE) AS old_acoa,
715
+ first_name,
716
+ last_name,
717
+ headline,
718
+ summary,
719
+ location,
720
+ profile_picture_url,
721
+ profile_image_cloudfront_url,
722
+ follower_count,
723
+ connections_count,
724
+ profiles_updated_at,
725
+ created_at
726
+ `;
727
+ // Better approach: two queries in transaction for clarity
728
+ const currentSql = `SELECT linkedin_identifier_acoa FROM linkedin.accounts WHERE forager_id = $1`;
729
+ const currentResult = await (0, pg_client_1.queryOne)(db, currentSql, [foragerId]);
730
+ if (!currentResult) {
731
+ throw new Error(`LinkedIn account with Forager ID ${foragerId} not found`);
732
+ }
733
+ const oldValue = currentResult.linkedin_identifier_acoa;
734
+ const wasChanged = oldValue !== linkedinIdentifierAcoa;
735
+ // Update the account
736
+ const updateSql = `
737
+ UPDATE linkedin.accounts
738
+ SET
739
+ linkedin_identifier_acoa = $2,
740
+ profiles_updated_at = NOW()
741
+ WHERE forager_id = $1
742
+ RETURNING
743
+ id,
744
+ user_id,
745
+ forager_id,
746
+ public_identifier,
747
+ linkedin_identifier_acoa,
748
+ first_name,
749
+ last_name,
750
+ headline,
751
+ summary,
752
+ location,
753
+ profile_picture_url,
754
+ profile_image_cloudfront_url,
755
+ follower_count,
756
+ connections_count,
757
+ profiles_updated_at,
758
+ created_at
759
+ `;
760
+ const updatedAccount = await (0, pg_client_1.queryOne)(db, updateSql, [foragerId, linkedinIdentifierAcoa]);
761
+ if (!updatedAccount) {
762
+ throw new Error(`Failed to update LinkedIn account with Forager ID ${foragerId}`);
763
+ }
764
+ return {
765
+ account: {
766
+ ...mapToLinkedInAccountResult(updatedAccount),
767
+ profile_image_cloudfront_url: null,
768
+ },
769
+ wasChanged,
770
+ oldValue,
771
+ };
772
+ }
773
+ catch (error) {
774
+ console.error("Error updating LinkedIn account ACoA identifier by Forager ID with change detection:", error);
775
+ throw error;
776
+ }
777
+ }
778
+ //# sourceMappingURL=forager-linkedin-operations.js.map