@twentyhq/people-data-labs 1.0.0

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 (341) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.nvmrc +1 -0
  3. package/.oxlintrc.json +58 -0
  4. package/.yarnrc.yml +1 -0
  5. package/README.md +16 -0
  6. package/package.json +37 -0
  7. package/public/gallery/cover.png +0 -0
  8. package/public/people-data-labs-icon.png +0 -0
  9. package/src/__tests__/global-setup.ts +87 -0
  10. package/src/__tests__/people-data-labs.integration-test.ts +25 -0
  11. package/src/application-config.ts +18 -0
  12. package/src/command-menu-items/enrich-companies.ts +14 -0
  13. package/src/command-menu-items/enrich-people.ts +14 -0
  14. package/src/constants/__tests__/select-option-constants.spec.ts +142 -0
  15. package/src/constants/billing-margin-multiplier.ts +1 -0
  16. package/src/constants/company-match-cost-dollars.ts +1 -0
  17. package/src/constants/company-type-options.ts +46 -0
  18. package/src/constants/enrichment-status-options.ts +19 -0
  19. package/src/constants/enrichment-workflow-seeds.ts +27 -0
  20. package/src/constants/funding-stage-options.ts +195 -0
  21. package/src/constants/industry-options.ts +1033 -0
  22. package/src/constants/inferred-salary-options.ts +81 -0
  23. package/src/constants/job-role-options.ts +172 -0
  24. package/src/constants/job-title-class-options.ts +39 -0
  25. package/src/constants/job-title-sub-role-options.ts +746 -0
  26. package/src/constants/location-continent-options.ts +53 -0
  27. package/src/constants/metro-options.ts +2692 -0
  28. package/src/constants/mic-exchange-options.ts +494 -0
  29. package/src/constants/micro-credits-per-dollar.ts +1 -0
  30. package/src/constants/pdl-base-url.ts +1 -0
  31. package/src/constants/person-match-cost-dollars.ts +1 -0
  32. package/src/constants/seniority-options.ts +74 -0
  33. package/src/constants/sex-options.ts +18 -0
  34. package/src/constants/size-options.ts +60 -0
  35. package/src/constants/universal-identifiers.ts +1516 -0
  36. package/src/constants/update-fields-option-values.ts +5 -0
  37. package/src/constants/update-fields-options.ts +7 -0
  38. package/src/fields/company/pdl-affiliated-profiles.field.ts +20 -0
  39. package/src/fields/company/pdl-alternative-domains.field.ts +20 -0
  40. package/src/fields/company/pdl-alternative-names.field.ts +20 -0
  41. package/src/fields/company/pdl-company-type.field.ts +28 -0
  42. package/src/fields/company/pdl-employee-count-by-country.field.ts +21 -0
  43. package/src/fields/company/pdl-employee-count.field.ts +19 -0
  44. package/src/fields/company/pdl-enrichment-status.field.ts +29 -0
  45. package/src/fields/company/pdl-facebook-url.field.ts +19 -0
  46. package/src/fields/company/pdl-founded-year.field.ts +19 -0
  47. package/src/fields/company/pdl-funding-stages.field.ts +28 -0
  48. package/src/fields/company/pdl-headline.field.ts +19 -0
  49. package/src/fields/company/pdl-id.field.ts +19 -0
  50. package/src/fields/company/pdl-industry-detail.field.ts +20 -0
  51. package/src/fields/company/pdl-industry.field.ts +28 -0
  52. package/src/fields/company/pdl-last-enriched-at.field.ts +20 -0
  53. package/src/fields/company/pdl-last-funding-date.field.ts +20 -0
  54. package/src/fields/company/pdl-latest-funding-stage.field.ts +29 -0
  55. package/src/fields/company/pdl-legal-name.field.ts +19 -0
  56. package/src/fields/company/pdl-linkedin-id.field.ts +19 -0
  57. package/src/fields/company/pdl-location-continent.field.ts +29 -0
  58. package/src/fields/company/pdl-location-metro.field.ts +28 -0
  59. package/src/fields/company/pdl-mic-exchange.field.ts +28 -0
  60. package/src/fields/company/pdl-naics.field.ts +19 -0
  61. package/src/fields/company/pdl-number-funding-rounds.field.ts +20 -0
  62. package/src/fields/company/pdl-raw-payload.field.ts +19 -0
  63. package/src/fields/company/pdl-sic.field.ts +19 -0
  64. package/src/fields/company/pdl-size-range.field.ts +28 -0
  65. package/src/fields/company/pdl-summary.field.ts +19 -0
  66. package/src/fields/company/pdl-tags.field.ts +19 -0
  67. package/src/fields/company/pdl-ticker.field.ts +19 -0
  68. package/src/fields/company/pdl-total-funding.field.ts +19 -0
  69. package/src/fields/company/pdl-twitter-url.field.ts +19 -0
  70. package/src/fields/person/pdl-birth-date.field.ts +19 -0
  71. package/src/fields/person/pdl-birth-year.field.ts +19 -0
  72. package/src/fields/person/pdl-certifications.field.ts +19 -0
  73. package/src/fields/person/pdl-education.field.ts +19 -0
  74. package/src/fields/person/pdl-enrichment-status.field.ts +29 -0
  75. package/src/fields/person/pdl-experience.field.ts +19 -0
  76. package/src/fields/person/pdl-facebook-url.field.ts +19 -0
  77. package/src/fields/person/pdl-github-url.field.ts +19 -0
  78. package/src/fields/person/pdl-headline.field.ts +19 -0
  79. package/src/fields/person/pdl-id.field.ts +19 -0
  80. package/src/fields/person/pdl-industry.field.ts +28 -0
  81. package/src/fields/person/pdl-inferred-salary.field.ts +28 -0
  82. package/src/fields/person/pdl-interests.field.ts +19 -0
  83. package/src/fields/person/pdl-job-onet-code.field.ts +19 -0
  84. package/src/fields/person/pdl-job-role.field.ts +28 -0
  85. package/src/fields/person/pdl-job-start-date.field.ts +19 -0
  86. package/src/fields/person/pdl-job-summary.field.ts +19 -0
  87. package/src/fields/person/pdl-job-title-class.field.ts +28 -0
  88. package/src/fields/person/pdl-job-title-sub-role.field.ts +29 -0
  89. package/src/fields/person/pdl-languages.field.ts +19 -0
  90. package/src/fields/person/pdl-last-enriched-at.field.ts +19 -0
  91. package/src/fields/person/pdl-likelihood.field.ts +19 -0
  92. package/src/fields/person/pdl-linkedin-connections.field.ts +20 -0
  93. package/src/fields/person/pdl-linkedin-username.field.ts +20 -0
  94. package/src/fields/person/pdl-location-metro.field.ts +28 -0
  95. package/src/fields/person/pdl-location.field.ts +19 -0
  96. package/src/fields/person/pdl-name-aliases.field.ts +19 -0
  97. package/src/fields/person/pdl-profiles.field.ts +19 -0
  98. package/src/fields/person/pdl-raw-payload.field.ts +19 -0
  99. package/src/fields/person/pdl-seniority.field.ts +28 -0
  100. package/src/fields/person/pdl-sex.field.ts +28 -0
  101. package/src/fields/person/pdl-skills.field.ts +19 -0
  102. package/src/fields/person/pdl-summary.field.ts +19 -0
  103. package/src/fields/person/pdl-twitter-url.field.ts +19 -0
  104. package/src/fields/person/pdl-years-experience.field.ts +21 -0
  105. package/src/front-components/enrich-companies-component-effect.tsx +21 -0
  106. package/src/front-components/enrich-people-component-effect.tsx +21 -0
  107. package/src/front-components/utils/call-enrich-logic-function.utils.ts +21 -0
  108. package/src/logic-functions/__mocks__/company-node.mock.ts +11 -0
  109. package/src/logic-functions/__mocks__/create-core-api-client-mock.ts +35 -0
  110. package/src/logic-functions/__mocks__/pdl-company-data.mock.ts +27 -0
  111. package/src/logic-functions/__mocks__/pdl-person-data.mock.ts +23 -0
  112. package/src/logic-functions/__mocks__/person-node.mock.ts +12 -0
  113. package/src/logic-functions/enrich-companies.function.ts +76 -0
  114. package/src/logic-functions/enrich-company.function.ts +73 -0
  115. package/src/logic-functions/enrich-people.function.ts +76 -0
  116. package/src/logic-functions/enrich-person.function.ts +73 -0
  117. package/src/logic-functions/errors/__tests__/pdl-config-error.spec.ts +16 -0
  118. package/src/logic-functions/errors/__tests__/pdl-invalid-input-error.spec.ts +16 -0
  119. package/src/logic-functions/errors/__tests__/pdl-operation-error.spec.ts +16 -0
  120. package/src/logic-functions/errors/__tests__/pdl-record-not-found-error.spec.ts +21 -0
  121. package/src/logic-functions/errors/pdl-config-error.ts +8 -0
  122. package/src/logic-functions/errors/pdl-error-code.ts +8 -0
  123. package/src/logic-functions/errors/pdl-error.ts +12 -0
  124. package/src/logic-functions/errors/pdl-invalid-input-error.ts +8 -0
  125. package/src/logic-functions/errors/pdl-operation-error.ts +8 -0
  126. package/src/logic-functions/errors/pdl-record-not-found-error.ts +22 -0
  127. package/src/logic-functions/handlers/__tests__/enrich-companies.spec.ts +159 -0
  128. package/src/logic-functions/handlers/__tests__/enrich-company.spec.ts +147 -0
  129. package/src/logic-functions/handlers/__tests__/enrich-people.spec.ts +313 -0
  130. package/src/logic-functions/handlers/__tests__/enrich-person.spec.ts +198 -0
  131. package/src/logic-functions/handlers/__tests__/post-install.spec.ts +153 -0
  132. package/src/logic-functions/handlers/company-enrichment-adapter.ts +42 -0
  133. package/src/logic-functions/handlers/company-single-enrichment-adapter.ts +15 -0
  134. package/src/logic-functions/handlers/enrich-companies.ts +15 -0
  135. package/src/logic-functions/handlers/enrich-company.ts +19 -0
  136. package/src/logic-functions/handlers/enrich-people.ts +15 -0
  137. package/src/logic-functions/handlers/enrich-person.ts +19 -0
  138. package/src/logic-functions/handlers/person-enrichment-adapter.ts +29 -0
  139. package/src/logic-functions/handlers/person-single-enrichment-adapter.ts +15 -0
  140. package/src/logic-functions/handlers/post-install.ts +69 -0
  141. package/src/logic-functions/post-install.function.ts +17 -0
  142. package/src/logic-functions/utils/__tests__/aggregate-bulk-enrich-result.spec.ts +52 -0
  143. package/src/logic-functions/utils/__tests__/build-address.spec.ts +30 -0
  144. package/src/logic-functions/utils/__tests__/build-allowed-values.spec.ts +16 -0
  145. package/src/logic-functions/utils/__tests__/build-bulk-records-trigger.spec.ts +26 -0
  146. package/src/logic-functions/utils/__tests__/build-company-create-data.spec.ts +37 -0
  147. package/src/logic-functions/utils/__tests__/build-company-match-keys.spec.ts +35 -0
  148. package/src/logic-functions/utils/__tests__/build-company-matched-data.spec.ts +56 -0
  149. package/src/logic-functions/utils/__tests__/build-currency-from-usd.spec.ts +17 -0
  150. package/src/logic-functions/utils/__tests__/build-emails.spec.ts +23 -0
  151. package/src/logic-functions/utils/__tests__/build-error-result.spec.ts +16 -0
  152. package/src/logic-functions/utils/__tests__/build-full-name.spec.ts +64 -0
  153. package/src/logic-functions/utils/__tests__/build-links.spec.ts +25 -0
  154. package/src/logic-functions/utils/__tests__/build-logic-function-step.spec.ts +33 -0
  155. package/src/logic-functions/utils/__tests__/build-matched-result.spec.ts +33 -0
  156. package/src/logic-functions/utils/__tests__/build-not-found-result.spec.ts +15 -0
  157. package/src/logic-functions/utils/__tests__/build-person-matched-data.spec.ts +117 -0
  158. package/src/logic-functions/utils/__tests__/build-person-name-param.spec.ts +17 -0
  159. package/src/logic-functions/utils/__tests__/build-phones.spec.ts +32 -0
  160. package/src/logic-functions/utils/__tests__/build-skipped-result.spec.ts +17 -0
  161. package/src/logic-functions/utils/__tests__/capitalize-name.spec.ts +26 -0
  162. package/src/logic-functions/utils/__tests__/capitalize-name.test.ts +17 -0
  163. package/src/logic-functions/utils/__tests__/charge-matched-enrichments.spec.ts +56 -0
  164. package/src/logic-functions/utils/__tests__/chunk.spec.ts +25 -0
  165. package/src/logic-functions/utils/__tests__/enrich-companies.spec.ts +26 -0
  166. package/src/logic-functions/utils/__tests__/enrich-company.spec.ts +54 -0
  167. package/src/logic-functions/utils/__tests__/enrich-people.spec.ts +32 -0
  168. package/src/logic-functions/utils/__tests__/enrich-person.spec.ts +62 -0
  169. package/src/logic-functions/utils/__tests__/extract-company-match-params.spec.ts +86 -0
  170. package/src/logic-functions/utils/__tests__/extract-pdl-error-message.spec.ts +17 -0
  171. package/src/logic-functions/utils/__tests__/extract-person-match-params.spec.ts +75 -0
  172. package/src/logic-functions/utils/__tests__/extract-record-ids.spec.ts +32 -0
  173. package/src/logic-functions/utils/__tests__/find-company-id-by-filter.spec.ts +42 -0
  174. package/src/logic-functions/utils/__tests__/find-company-id.spec.ts +118 -0
  175. package/src/logic-functions/utils/__tests__/find-existing-workflow-id.spec.ts +26 -0
  176. package/src/logic-functions/utils/__tests__/find-or-create-current-company.spec.ts +203 -0
  177. package/src/logic-functions/utils/__tests__/get-pdl-api-key.spec.ts +38 -0
  178. package/src/logic-functions/utils/__tests__/is-empty-address.spec.ts +20 -0
  179. package/src/logic-functions/utils/__tests__/is-empty-emails.spec.ts +14 -0
  180. package/src/logic-functions/utils/__tests__/is-empty-full-name.spec.ts +14 -0
  181. package/src/logic-functions/utils/__tests__/is-empty-links.spec.ts +14 -0
  182. package/src/logic-functions/utils/__tests__/is-empty-phones.spec.ts +14 -0
  183. package/src/logic-functions/utils/__tests__/is-empty-text.spec.ts +15 -0
  184. package/src/logic-functions/utils/__tests__/map-company.spec.ts +48 -0
  185. package/src/logic-functions/utils/__tests__/map-person.spec.ts +81 -0
  186. package/src/logic-functions/utils/__tests__/normalize-domain.spec.ts +19 -0
  187. package/src/logic-functions/utils/__tests__/normalize-linkedin-url.spec.ts +22 -0
  188. package/src/logic-functions/utils/__tests__/now-iso.spec.ts +9 -0
  189. package/src/logic-functions/utils/__tests__/parse-geo.spec.ts +21 -0
  190. package/src/logic-functions/utils/__tests__/parse-partial-date.spec.ts +29 -0
  191. package/src/logic-functions/utils/__tests__/parse-pdl-item.spec.ts +88 -0
  192. package/src/logic-functions/utils/__tests__/pick-multi-select.spec.ts +28 -0
  193. package/src/logic-functions/utils/__tests__/pick-select.spec.ts +38 -0
  194. package/src/logic-functions/utils/__tests__/pick-writable-standard.spec.ts +37 -0
  195. package/src/logic-functions/utils/__tests__/post-pdl-enrich.spec.ts +192 -0
  196. package/src/logic-functions/utils/__tests__/post-pdl-single-enrich.spec.ts +172 -0
  197. package/src/logic-functions/utils/__tests__/read-companies.spec.ts +39 -0
  198. package/src/logic-functions/utils/__tests__/read-people.spec.ts +40 -0
  199. package/src/logic-functions/utils/__tests__/resolve-logic-function-id.spec.ts +25 -0
  200. package/src/logic-functions/utils/__tests__/resolve-update-fields-mode.spec.ts +34 -0
  201. package/src/logic-functions/utils/__tests__/run-batch-enrichment.spec.ts +524 -0
  202. package/src/logic-functions/utils/__tests__/salary-transform.spec.ts +15 -0
  203. package/src/logic-functions/utils/__tests__/seed-enrichment-workflow.spec.ts +136 -0
  204. package/src/logic-functions/utils/__tests__/size-transform.spec.ts +18 -0
  205. package/src/logic-functions/utils/__tests__/to-json-array.spec.ts +16 -0
  206. package/src/logic-functions/utils/__tests__/to-json-object.spec.ts +18 -0
  207. package/src/logic-functions/utils/__tests__/to-number.spec.ts +17 -0
  208. package/src/logic-functions/utils/__tests__/to-string-array.spec.ts +17 -0
  209. package/src/logic-functions/utils/__tests__/to-text.spec.ts +20 -0
  210. package/src/logic-functions/utils/__tests__/update-companies-status.spec.ts +32 -0
  211. package/src/logic-functions/utils/__tests__/update-company-record.spec.ts +25 -0
  212. package/src/logic-functions/utils/__tests__/update-people-status.spec.ts +32 -0
  213. package/src/logic-functions/utils/__tests__/update-person-record.spec.ts +25 -0
  214. package/src/logic-functions/utils/aggregate-bulk-enrich-result.ts +21 -0
  215. package/src/logic-functions/utils/build-address.ts +40 -0
  216. package/src/logic-functions/utils/build-allowed-values.ts +5 -0
  217. package/src/logic-functions/utils/build-bulk-records-trigger.ts +25 -0
  218. package/src/logic-functions/utils/build-company-create-data.ts +35 -0
  219. package/src/logic-functions/utils/build-company-match-keys.ts +16 -0
  220. package/src/logic-functions/utils/build-company-matched-data.ts +56 -0
  221. package/src/logic-functions/utils/build-currency-from-usd.ts +16 -0
  222. package/src/logic-functions/utils/build-emails.ts +34 -0
  223. package/src/logic-functions/utils/build-error-result.ts +18 -0
  224. package/src/logic-functions/utils/build-full-name.ts +38 -0
  225. package/src/logic-functions/utils/build-links.ts +23 -0
  226. package/src/logic-functions/utils/build-logic-function-step.ts +30 -0
  227. package/src/logic-functions/utils/build-matched-result.ts +21 -0
  228. package/src/logic-functions/utils/build-not-found-result.ts +9 -0
  229. package/src/logic-functions/utils/build-person-matched-data.ts +78 -0
  230. package/src/logic-functions/utils/build-person-name-param.ts +19 -0
  231. package/src/logic-functions/utils/build-phones.ts +40 -0
  232. package/src/logic-functions/utils/build-skipped-result.ts +15 -0
  233. package/src/logic-functions/utils/capitalize-name.ts +8 -0
  234. package/src/logic-functions/utils/charge-matched-enrichments.ts +29 -0
  235. package/src/logic-functions/utils/chunk.ts +18 -0
  236. package/src/logic-functions/utils/enrich-chunk.ts +361 -0
  237. package/src/logic-functions/utils/enrich-companies.ts +21 -0
  238. package/src/logic-functions/utils/enrich-company.ts +23 -0
  239. package/src/logic-functions/utils/enrich-people.ts +22 -0
  240. package/src/logic-functions/utils/enrich-person.ts +24 -0
  241. package/src/logic-functions/utils/extract-company-match-params.ts +52 -0
  242. package/src/logic-functions/utils/extract-pdl-error-message.ts +47 -0
  243. package/src/logic-functions/utils/extract-person-match-params.ts +59 -0
  244. package/src/logic-functions/utils/extract-record-ids.ts +18 -0
  245. package/src/logic-functions/utils/find-company-id-by-filter.ts +28 -0
  246. package/src/logic-functions/utils/find-company-id.ts +59 -0
  247. package/src/logic-functions/utils/find-existing-workflow-id.ts +18 -0
  248. package/src/logic-functions/utils/find-or-create-current-company.ts +108 -0
  249. package/src/logic-functions/utils/get-pdl-api-key.ts +15 -0
  250. package/src/logic-functions/utils/internal-field-names.ts +6 -0
  251. package/src/logic-functions/utils/is-empty-address.ts +20 -0
  252. package/src/logic-functions/utils/is-empty-emails.ts +13 -0
  253. package/src/logic-functions/utils/is-empty-full-name.ts +13 -0
  254. package/src/logic-functions/utils/is-empty-links.ts +13 -0
  255. package/src/logic-functions/utils/is-empty-phones.ts +13 -0
  256. package/src/logic-functions/utils/is-empty-text.ts +4 -0
  257. package/src/logic-functions/utils/map-company.ts +115 -0
  258. package/src/logic-functions/utils/map-person.ts +136 -0
  259. package/src/logic-functions/utils/normalize-domain.ts +22 -0
  260. package/src/logic-functions/utils/normalize-linkedin-url.ts +23 -0
  261. package/src/logic-functions/utils/now-iso.ts +1 -0
  262. package/src/logic-functions/utils/parse-geo.ts +33 -0
  263. package/src/logic-functions/utils/parse-partial-date.ts +33 -0
  264. package/src/logic-functions/utils/parse-pdl-item.ts +82 -0
  265. package/src/logic-functions/utils/pick-multi-select.ts +32 -0
  266. package/src/logic-functions/utils/pick-select.ts +30 -0
  267. package/src/logic-functions/utils/pick-writable-standard.ts +27 -0
  268. package/src/logic-functions/utils/post-pdl-enrich.ts +89 -0
  269. package/src/logic-functions/utils/post-pdl-single-enrich.ts +59 -0
  270. package/src/logic-functions/utils/read-companies.ts +53 -0
  271. package/src/logic-functions/utils/read-people.ts +48 -0
  272. package/src/logic-functions/utils/resolve-logic-function-id.ts +11 -0
  273. package/src/logic-functions/utils/resolve-min-likelihood.ts +12 -0
  274. package/src/logic-functions/utils/resolve-update-fields-mode.ts +16 -0
  275. package/src/logic-functions/utils/run-batch-enrichment.ts +50 -0
  276. package/src/logic-functions/utils/run-single-enrichment.ts +44 -0
  277. package/src/logic-functions/utils/salary-transform.ts +18 -0
  278. package/src/logic-functions/utils/seed-enrichment-workflow.ts +131 -0
  279. package/src/logic-functions/utils/size-transform.ts +15 -0
  280. package/src/logic-functions/utils/to-bulk-enrich-input.ts +14 -0
  281. package/src/logic-functions/utils/to-json-array.ts +4 -0
  282. package/src/logic-functions/utils/to-json-object.ts +22 -0
  283. package/src/logic-functions/utils/to-number.ts +4 -0
  284. package/src/logic-functions/utils/to-string-array.ts +28 -0
  285. package/src/logic-functions/utils/to-text.ts +11 -0
  286. package/src/logic-functions/utils/update-companies-status.ts +18 -0
  287. package/src/logic-functions/utils/update-company-record.ts +15 -0
  288. package/src/logic-functions/utils/update-people-status.ts +18 -0
  289. package/src/logic-functions/utils/update-person-record.ts +15 -0
  290. package/src/roles/default-function.role.ts +41 -0
  291. package/src/types/address-parts.ts +9 -0
  292. package/src/types/address-value.ts +10 -0
  293. package/src/types/batch-enrichment-adapter.ts +43 -0
  294. package/src/types/bulk-enrich-input.ts +9 -0
  295. package/src/types/bulk-enrich-result.ts +11 -0
  296. package/src/types/company-id-by-match-key-cache.ts +1 -0
  297. package/src/types/company-match-keys.ts +6 -0
  298. package/src/types/company-node.ts +16 -0
  299. package/src/types/currency-value.ts +4 -0
  300. package/src/types/emails-value.ts +4 -0
  301. package/src/types/enrich-result.ts +11 -0
  302. package/src/types/enrich-status.ts +1 -0
  303. package/src/types/enrichment-workflow-seed.ts +9 -0
  304. package/src/types/full-name-value.ts +4 -0
  305. package/src/types/links-value.ts +5 -0
  306. package/src/types/mapped-record.ts +4 -0
  307. package/src/types/pdl-company-data.ts +50 -0
  308. package/src/types/pdl-company-enrich-params.ts +7 -0
  309. package/src/types/pdl-enrich-result.ts +4 -0
  310. package/src/types/pdl-person-data.ts +65 -0
  311. package/src/types/pdl-person-enrich-params.ts +8 -0
  312. package/src/types/person-node.ts +11 -0
  313. package/src/types/phones-value.ts +12 -0
  314. package/src/types/post-install-result.ts +5 -0
  315. package/src/types/seed-enrichment-workflow-result.ts +7 -0
  316. package/src/types/select-option-meta.ts +9 -0
  317. package/src/types/single-enrich-input.ts +7 -0
  318. package/src/types/tag-color.ts +26 -0
  319. package/src/types/update-fields-option.ts +4 -0
  320. package/src/types/workflow-bulk-records-trigger.ts +15 -0
  321. package/src/types/workflow-logic-function-step.ts +18 -0
  322. package/src/utils/__tests__/build-select-options.spec.ts +20 -0
  323. package/src/utils/__tests__/is-defined.spec.ts +16 -0
  324. package/src/utils/__tests__/is-unique-violation-error.spec.ts +71 -0
  325. package/src/utils/__tests__/normalize-enum-value.spec.ts +28 -0
  326. package/src/utils/__tests__/prune-undefined.spec.ts +22 -0
  327. package/src/utils/__tests__/strip-separators.spec.ts +13 -0
  328. package/src/utils/__tests__/to-error-message.spec.ts +13 -0
  329. package/src/utils/build-select-options.ts +16 -0
  330. package/src/utils/is-defined.ts +5 -0
  331. package/src/utils/is-unique-violation-error.ts +74 -0
  332. package/src/utils/normalize-enum-value.ts +15 -0
  333. package/src/utils/prune-undefined.ts +15 -0
  334. package/src/utils/strip-separators.ts +4 -0
  335. package/src/utils/to-error-message.ts +2 -0
  336. package/src/views/enriched-companies.view.ts +69 -0
  337. package/src/views/enriched-people.view.ts +74 -0
  338. package/tsconfig.json +42 -0
  339. package/tsconfig.spec.json +9 -0
  340. package/vitest.config.ts +31 -0
  341. package/vitest.unit.config.ts +14 -0
@@ -0,0 +1,75 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { PERSON_NODE_MOCK } from 'src/logic-functions/__mocks__/person-node.mock';
4
+ import { extractPersonMatchParams } from 'src/logic-functions/utils/extract-person-match-params';
5
+
6
+ describe('extractPersonMatchParams', () => {
7
+ it('prefers an existing pdlId and uses the strong-identifier likelihood', () => {
8
+ expect(
9
+ extractPersonMatchParams({
10
+ node: { ...PERSON_NODE_MOCK, pdlId: 'pdl-1' },
11
+ input: { records: [] },
12
+ }),
13
+ ).toEqual({ pdlId: 'pdl-1', minLikelihood: 2 });
14
+ });
15
+
16
+ it('uses the linkedin profile with the strong-identifier likelihood', () => {
17
+ expect(
18
+ extractPersonMatchParams({
19
+ node: PERSON_NODE_MOCK,
20
+ input: { records: [] },
21
+ }),
22
+ ).toEqual({
23
+ profile: 'https://linkedin.com/in/existing',
24
+ minLikelihood: 2,
25
+ });
26
+ });
27
+
28
+ it('pairs a name with the person company and uses the weak-identifier likelihood', () => {
29
+ expect(
30
+ extractPersonMatchParams({
31
+ node: {
32
+ ...PERSON_NODE_MOCK,
33
+ linkedinLink: null,
34
+ name: { firstName: 'Jane', lastName: 'Doe' },
35
+ company: { id: 'co-1', name: 'Acme' },
36
+ },
37
+ input: { records: [] },
38
+ }),
39
+ ).toEqual({ name: 'Jane Doe', company: 'Acme', minLikelihood: 6 });
40
+ });
41
+
42
+ it('returns undefined for a name with no anchoring identifier or company', () => {
43
+ expect(
44
+ extractPersonMatchParams({
45
+ node: {
46
+ ...PERSON_NODE_MOCK,
47
+ linkedinLink: null,
48
+ name: { firstName: 'Jane', lastName: 'Doe' },
49
+ },
50
+ input: { records: [] },
51
+ }),
52
+ ).toBeUndefined();
53
+ });
54
+
55
+ it('honors an explicit minLikelihood from the input', () => {
56
+ expect(
57
+ extractPersonMatchParams({
58
+ node: PERSON_NODE_MOCK,
59
+ input: {
60
+ records: [],
61
+ minLikelihood: 9,
62
+ },
63
+ }),
64
+ ).toMatchObject({ minLikelihood: 9 });
65
+ });
66
+
67
+ it('returns undefined when there is no usable identifier', () => {
68
+ expect(
69
+ extractPersonMatchParams({
70
+ node: { ...PERSON_NODE_MOCK, linkedinLink: null },
71
+ input: { records: [] },
72
+ }),
73
+ ).toBeUndefined();
74
+ });
75
+ });
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { extractRecordIds } from 'src/logic-functions/utils/extract-record-ids';
4
+
5
+ describe('extractRecordIds', () => {
6
+ it('extracts ids from record objects', () => {
7
+ expect(extractRecordIds([{ id: 'a' }, { id: 'b' }])).toEqual(['a', 'b']);
8
+ });
9
+
10
+ it('accepts plain id strings', () => {
11
+ expect(extractRecordIds(['a', 'b'])).toEqual(['a', 'b']);
12
+ });
13
+
14
+ it('drops entries without a usable id', () => {
15
+ expect(
16
+ extractRecordIds([{ id: 'a' }, { id: null }, {}, { id: '' }, 'c']),
17
+ ).toEqual(['a', 'c']);
18
+ });
19
+
20
+ it('accepts a single record object (not wrapped in an array)', () => {
21
+ expect(extractRecordIds({ id: 'a' })).toEqual(['a']);
22
+ });
23
+
24
+ it('accepts a single id string (not wrapped in an array)', () => {
25
+ expect(extractRecordIds('a')).toEqual(['a']);
26
+ });
27
+
28
+ it('returns an empty array for nullish input', () => {
29
+ expect(extractRecordIds(undefined as unknown as [])).toEqual([]);
30
+ expect(extractRecordIds(null as unknown as [])).toEqual([]);
31
+ });
32
+ });
@@ -0,0 +1,42 @@
1
+ import { type CoreApiClient } from 'twenty-client-sdk/core';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+
4
+ import { findCompanyIdByFilter } from 'src/logic-functions/utils/find-company-id-by-filter';
5
+
6
+ describe('findCompanyIdByFilter', () => {
7
+ it('returns the first matching company id and forwards the filter', async () => {
8
+ const query = vi.fn(() =>
9
+ Promise.resolve({ companies: { edges: [{ node: { id: 'co-1' } }] } }),
10
+ );
11
+ const client = { query } as unknown as CoreApiClient;
12
+ const filter = { pdlId: { eq: 'pdl-co-1' } };
13
+
14
+ const result = await findCompanyIdByFilter({ client, filter });
15
+
16
+ expect(result).toBe('co-1');
17
+ expect(query).toHaveBeenCalledWith({
18
+ companies: {
19
+ __args: { filter, first: 1 },
20
+ edges: { node: { id: true } },
21
+ },
22
+ });
23
+ });
24
+
25
+ it('returns undefined when there are no matches', async () => {
26
+ const query = vi.fn(() => Promise.resolve({ companies: { edges: [] } }));
27
+ const client = { query } as unknown as CoreApiClient;
28
+
29
+ expect(
30
+ await findCompanyIdByFilter({ client, filter: { name: { eq: 'Acme' } } }),
31
+ ).toBeUndefined();
32
+ });
33
+
34
+ it('returns undefined when the companies connection is absent', async () => {
35
+ const query = vi.fn(() => Promise.resolve({}));
36
+ const client = { query } as unknown as CoreApiClient;
37
+
38
+ expect(
39
+ await findCompanyIdByFilter({ client, filter: { name: { eq: 'Acme' } } }),
40
+ ).toBeUndefined();
41
+ });
42
+ });
@@ -0,0 +1,118 @@
1
+ import { type CoreApiClient } from 'twenty-client-sdk/core';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+
4
+ import { findCompanyId } from 'src/logic-functions/utils/find-company-id';
5
+
6
+ type CompanyQueryRequest = {
7
+ companies: { __args: { filter: Record<string, unknown> } };
8
+ };
9
+
10
+ const filterKeyOf = (request: unknown) =>
11
+ Object.keys((request as CompanyQueryRequest).companies.__args.filter)[0];
12
+
13
+ const ALL_KEYS = {
14
+ pdlId: 'pdl-co-1',
15
+ website: 'acme.com',
16
+ linkedinUrl: 'https://linkedin.com/company/acme',
17
+ name: 'Acme',
18
+ };
19
+
20
+ describe('findCompanyId', () => {
21
+ it('matches by pdlId first and does not query the lower-priority keys', async () => {
22
+ const query = vi.fn((request: unknown) =>
23
+ Promise.resolve(
24
+ filterKeyOf(request) === 'pdlId'
25
+ ? { companies: { edges: [{ node: { id: 'co-pdl' } }] } }
26
+ : { companies: { edges: [] } },
27
+ ),
28
+ );
29
+ const client = { query } as unknown as CoreApiClient;
30
+
31
+ const result = await findCompanyId({ client, matchKeys: ALL_KEYS });
32
+
33
+ expect(result).toBe('co-pdl');
34
+ expect(query).toHaveBeenCalledTimes(1);
35
+ });
36
+
37
+ it('falls through to lower-priority keys in order until one matches', async () => {
38
+ const queriedKeys: string[] = [];
39
+ const query = vi.fn((request: unknown) => {
40
+ const key = filterKeyOf(request);
41
+ queriedKeys.push(key);
42
+
43
+ return Promise.resolve(
44
+ key === 'name'
45
+ ? { companies: { edges: [{ node: { id: 'co-name' } }] } }
46
+ : { companies: { edges: [] } },
47
+ );
48
+ });
49
+ const client = { query } as unknown as CoreApiClient;
50
+
51
+ const result = await findCompanyId({ client, matchKeys: ALL_KEYS });
52
+
53
+ expect(result).toBe('co-name');
54
+ expect(queriedKeys).toEqual([
55
+ 'pdlId',
56
+ 'domainName',
57
+ 'linkedinLink',
58
+ 'name',
59
+ ]);
60
+ });
61
+
62
+ it('skips filters whose match key is absent', async () => {
63
+ const queriedKeys: string[] = [];
64
+ const query = vi.fn((request: unknown) => {
65
+ queriedKeys.push(filterKeyOf(request));
66
+
67
+ return Promise.resolve({ companies: { edges: [] } });
68
+ });
69
+ const client = { query } as unknown as CoreApiClient;
70
+
71
+ await findCompanyId({ client, matchKeys: { name: 'Acme' } });
72
+
73
+ expect(queriedKeys).toEqual(['name']);
74
+ });
75
+
76
+ it('returns undefined when no key matches', async () => {
77
+ const query = vi.fn(() => Promise.resolve({ companies: { edges: [] } }));
78
+ const client = { query } as unknown as CoreApiClient;
79
+
80
+ expect(
81
+ await findCompanyId({ client, matchKeys: ALL_KEYS }),
82
+ ).toBeUndefined();
83
+ });
84
+
85
+ it('returns undefined and issues no query when no keys are provided', async () => {
86
+ const query = vi.fn();
87
+ const client = { query } as unknown as CoreApiClient;
88
+
89
+ expect(await findCompanyId({ client, matchKeys: {} })).toBeUndefined();
90
+ expect(query).not.toHaveBeenCalled();
91
+ });
92
+
93
+ it('does not link a name match when it is ambiguous', async () => {
94
+ const query = vi.fn(() =>
95
+ Promise.resolve({
96
+ companies: {
97
+ edges: [{ node: { id: 'co-1' } }, { node: { id: 'co-2' } }],
98
+ },
99
+ }),
100
+ );
101
+ const client = { query } as unknown as CoreApiClient;
102
+
103
+ expect(
104
+ await findCompanyId({ client, matchKeys: { name: 'Acme' } }),
105
+ ).toBeUndefined();
106
+ });
107
+
108
+ it('links a unique name match', async () => {
109
+ const query = vi.fn(() =>
110
+ Promise.resolve({ companies: { edges: [{ node: { id: 'co-1' } }] } }),
111
+ );
112
+ const client = { query } as unknown as CoreApiClient;
113
+
114
+ expect(await findCompanyId({ client, matchKeys: { name: 'Acme' } })).toBe(
115
+ 'co-1',
116
+ );
117
+ });
118
+ });
@@ -0,0 +1,26 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { createCoreApiClientMock } from 'src/logic-functions/__mocks__/create-core-api-client-mock';
4
+ import { findExistingWorkflowId } from 'src/logic-functions/utils/find-existing-workflow-id';
5
+
6
+ describe('findExistingWorkflowId', () => {
7
+ it('returns the id of the first matching workflow', async () => {
8
+ const client = createCoreApiClientMock({
9
+ queryResult: { workflows: { edges: [{ node: { id: 'workflow-1' } }] } },
10
+ });
11
+
12
+ await expect(
13
+ findExistingWorkflowId({ client, name: 'Enrich company' }),
14
+ ).resolves.toBe('workflow-1');
15
+ });
16
+
17
+ it('returns undefined when no workflow matches', async () => {
18
+ const client = createCoreApiClientMock({
19
+ queryResult: { workflows: { edges: [] } },
20
+ });
21
+
22
+ await expect(
23
+ findExistingWorkflowId({ client, name: 'Enrich company' }),
24
+ ).resolves.toBeUndefined();
25
+ });
26
+ });
@@ -0,0 +1,203 @@
1
+ import { type CoreApiClient } from 'twenty-client-sdk/core';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+
4
+ import { findOrCreateCurrentCompany } from 'src/logic-functions/utils/find-or-create-current-company';
5
+ import { type PdlPersonData } from 'src/types/pdl-person-data';
6
+
7
+ type CompanyQueryRequest = {
8
+ companies: { __args: { filter: Record<string, unknown> } };
9
+ };
10
+ type CreateCompanyRequest = {
11
+ createCompany: { __args: { data: Record<string, unknown> } };
12
+ };
13
+
14
+ const filterOf = (request: unknown) =>
15
+ (request as CompanyQueryRequest).companies.__args.filter;
16
+ const createDataOf = (request: unknown) =>
17
+ (request as CreateCompanyRequest).createCompany.__args.data;
18
+
19
+ const newCache = () => new Map<string, string | undefined>();
20
+
21
+ describe('findOrCreateCurrentCompany', () => {
22
+ it('returns undefined and issues no calls when PDL gives no identifiers', async () => {
23
+ const query = vi.fn();
24
+ const mutation = vi.fn();
25
+ const client = { query, mutation } as unknown as CoreApiClient;
26
+
27
+ const result = await findOrCreateCurrentCompany({
28
+ client,
29
+ personData: {} as PdlPersonData,
30
+ companyIdByMatchKeyCache: newCache(),
31
+ });
32
+
33
+ expect(result).toBeUndefined();
34
+ expect(query).not.toHaveBeenCalled();
35
+ expect(mutation).not.toHaveBeenCalled();
36
+ });
37
+
38
+ it('links an existing company matched by pdlId alone', async () => {
39
+ const query = vi.fn(() =>
40
+ Promise.resolve({ companies: { edges: [{ node: { id: 'co-existing' } }] } }),
41
+ );
42
+ const mutation = vi.fn();
43
+ const client = { query, mutation } as unknown as CoreApiClient;
44
+
45
+ const result = await findOrCreateCurrentCompany({
46
+ client,
47
+ personData: { job_company_id: 'pdl-co-1' } as PdlPersonData,
48
+ companyIdByMatchKeyCache: newCache(),
49
+ });
50
+
51
+ expect(result).toBe('co-existing');
52
+ expect(mutation).not.toHaveBeenCalled();
53
+ });
54
+
55
+ it('attempts a pdlId match but does not create without a name or website', async () => {
56
+ const query = vi.fn(() => Promise.resolve({ companies: { edges: [] } }));
57
+ const mutation = vi.fn();
58
+ const client = { query, mutation } as unknown as CoreApiClient;
59
+
60
+ const result = await findOrCreateCurrentCompany({
61
+ client,
62
+ personData: { job_company_id: 'pdl-co-1' } as PdlPersonData,
63
+ companyIdByMatchKeyCache: newCache(),
64
+ });
65
+
66
+ expect(result).toBeUndefined();
67
+ expect(query).toHaveBeenCalled();
68
+ expect(mutation).not.toHaveBeenCalled();
69
+ });
70
+
71
+ it('returns an existing company id without creating one', async () => {
72
+ const query = vi.fn(() =>
73
+ Promise.resolve({ companies: { edges: [{ node: { id: 'co-existing' } }] } }),
74
+ );
75
+ const mutation = vi.fn();
76
+ const client = { query, mutation } as unknown as CoreApiClient;
77
+
78
+ const result = await findOrCreateCurrentCompany({
79
+ client,
80
+ personData: {
81
+ job_company_name: 'Acme',
82
+ } as PdlPersonData,
83
+ companyIdByMatchKeyCache: newCache(),
84
+ });
85
+
86
+ expect(result).toBe('co-existing');
87
+ expect(mutation).not.toHaveBeenCalled();
88
+ });
89
+
90
+ it('creates a company from the PDL data when none matches', async () => {
91
+ const query = vi.fn(() => Promise.resolve({ companies: { edges: [] } }));
92
+ let createdData: Record<string, unknown> | undefined;
93
+ const mutation = vi.fn((request: unknown) => {
94
+ createdData = createDataOf(request);
95
+
96
+ return Promise.resolve({ createCompany: { id: 'co-new' } });
97
+ });
98
+ const client = { query, mutation } as unknown as CoreApiClient;
99
+
100
+ const result = await findOrCreateCurrentCompany({
101
+ client,
102
+ personData: {
103
+ job_company_id: 'pdl-co-2',
104
+ job_company_name: 'Acme',
105
+ job_company_website: 'acme.com',
106
+ } as PdlPersonData,
107
+ companyIdByMatchKeyCache: newCache(),
108
+ });
109
+
110
+ expect(result).toBe('co-new');
111
+ expect(createdData).toMatchObject({ name: 'Acme', pdlId: 'pdl-co-2' });
112
+ });
113
+
114
+ it('throws when the create mutation returns no company id', async () => {
115
+ const query = vi.fn(() => Promise.resolve({ companies: { edges: [] } }));
116
+ const mutation = vi.fn(() => Promise.resolve({ createCompany: null }));
117
+ const client = { query, mutation } as unknown as CoreApiClient;
118
+
119
+ await expect(
120
+ findOrCreateCurrentCompany({
121
+ client,
122
+ personData: {
123
+ job_company_name: 'Acme',
124
+ } as PdlPersonData,
125
+ companyIdByMatchKeyCache: newCache(),
126
+ }),
127
+ ).rejects.toThrow('Failed to create company: no id returned.');
128
+ });
129
+
130
+ it('re-finds the winner when a concurrent create hits a unique violation', async () => {
131
+ let created = false;
132
+ const query = vi.fn((request: unknown) =>
133
+ Promise.resolve(
134
+ created && 'pdlId' in filterOf(request)
135
+ ? { companies: { edges: [{ node: { id: 'co-race' } }] } }
136
+ : { companies: { edges: [] } },
137
+ ),
138
+ );
139
+ const mutation = vi.fn(() => {
140
+ created = true;
141
+
142
+ return Promise.reject(
143
+ new Error('duplicate key value violates unique constraint'),
144
+ );
145
+ });
146
+ const client = { query, mutation } as unknown as CoreApiClient;
147
+
148
+ const result = await findOrCreateCurrentCompany({
149
+ client,
150
+ personData: {
151
+ job_company_id: 'pdl-co-3',
152
+ job_company_name: 'Acme',
153
+ } as PdlPersonData,
154
+ companyIdByMatchKeyCache: newCache(),
155
+ });
156
+
157
+ expect(result).toBe('co-race');
158
+ });
159
+
160
+ it('rethrows a non-unique-violation create error', async () => {
161
+ const query = vi.fn(() => Promise.resolve({ companies: { edges: [] } }));
162
+ const mutation = vi.fn(() => Promise.reject(new Error('network down')));
163
+ const client = { query, mutation } as unknown as CoreApiClient;
164
+
165
+ await expect(
166
+ findOrCreateCurrentCompany({
167
+ client,
168
+ personData: {
169
+ job_company_name: 'Acme',
170
+ } as PdlPersonData,
171
+ companyIdByMatchKeyCache: newCache(),
172
+ }),
173
+ ).rejects.toThrow('network down');
174
+ });
175
+
176
+ it('reuses a cached company id for an identical company without re-querying', async () => {
177
+ const query = vi.fn(() =>
178
+ Promise.resolve({ companies: { edges: [{ node: { id: 'co-existing' } }] } }),
179
+ );
180
+ const mutation = vi.fn();
181
+ const client = { query, mutation } as unknown as CoreApiClient;
182
+ const companyIdByMatchKeyCache = newCache();
183
+ const personData = {
184
+ job_company_name: 'Acme',
185
+ job_company_website: 'acme.com',
186
+ } as PdlPersonData;
187
+
188
+ const first = await findOrCreateCurrentCompany({
189
+ client,
190
+ personData,
191
+ companyIdByMatchKeyCache,
192
+ });
193
+ const second = await findOrCreateCurrentCompany({
194
+ client,
195
+ personData,
196
+ companyIdByMatchKeyCache,
197
+ });
198
+
199
+ expect(first).toBe('co-existing');
200
+ expect(second).toBe('co-existing');
201
+ expect(query).toHaveBeenCalledTimes(1);
202
+ });
203
+ });
@@ -0,0 +1,38 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
+
3
+ import { getPdlApiKey } from 'src/logic-functions/utils/get-pdl-api-key';
4
+ import { PdlConfigError } from 'src/logic-functions/errors/pdl-config-error';
5
+
6
+ describe('getPdlApiKey', () => {
7
+ let previousApiKey: string | undefined;
8
+
9
+ beforeEach(() => {
10
+ previousApiKey = process.env.PDL_API_KEY;
11
+ });
12
+
13
+ afterEach(() => {
14
+ if (previousApiKey === undefined) {
15
+ delete process.env.PDL_API_KEY;
16
+ } else {
17
+ process.env.PDL_API_KEY = previousApiKey;
18
+ }
19
+ });
20
+
21
+ it('returns the configured key', () => {
22
+ process.env.PDL_API_KEY = 'secret-key';
23
+
24
+ expect(getPdlApiKey()).toBe('secret-key');
25
+ });
26
+
27
+ it('throws a PdlConfigError when the key is missing', () => {
28
+ delete process.env.PDL_API_KEY;
29
+
30
+ expect(() => getPdlApiKey()).toThrow(PdlConfigError);
31
+ });
32
+
33
+ it('throws a PdlConfigError when the key is blank', () => {
34
+ process.env.PDL_API_KEY = ' ';
35
+
36
+ expect(() => getPdlApiKey()).toThrow(PdlConfigError);
37
+ });
38
+ });
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { isEmptyAddress } from 'src/logic-functions/utils/is-empty-address';
4
+
5
+ describe('isEmptyAddress', () => {
6
+ it('is true when every address text field is empty or missing', () => {
7
+ expect(isEmptyAddress(null)).toBe(true);
8
+ expect(
9
+ isEmptyAddress({
10
+ addressStreet1: '',
11
+ addressCity: ' ',
12
+ addressCountry: '',
13
+ }),
14
+ ).toBe(true);
15
+ });
16
+
17
+ it('is false when at least one address field is present', () => {
18
+ expect(isEmptyAddress({ addressCity: 'San Francisco' })).toBe(false);
19
+ });
20
+ });
@@ -0,0 +1,14 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { isEmptyEmails } from 'src/logic-functions/utils/is-empty-emails';
4
+
5
+ describe('isEmptyEmails', () => {
6
+ it('is true when there is no primary email', () => {
7
+ expect(isEmptyEmails(null)).toBe(true);
8
+ expect(isEmptyEmails({ primaryEmail: '' })).toBe(true);
9
+ });
10
+
11
+ it('is false when a primary email is present', () => {
12
+ expect(isEmptyEmails({ primaryEmail: 'jane@acme.com' })).toBe(false);
13
+ });
14
+ });
@@ -0,0 +1,14 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { isEmptyFullName } from 'src/logic-functions/utils/is-empty-full-name';
4
+
5
+ describe('isEmptyFullName', () => {
6
+ it('is true when both first and last name are empty or missing', () => {
7
+ expect(isEmptyFullName(null)).toBe(true);
8
+ expect(isEmptyFullName({ firstName: '', lastName: '' })).toBe(true);
9
+ });
10
+
11
+ it('is false when at least one name part is present', () => {
12
+ expect(isEmptyFullName({ firstName: 'Jane', lastName: '' })).toBe(false);
13
+ });
14
+ });
@@ -0,0 +1,14 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { isEmptyLinks } from 'src/logic-functions/utils/is-empty-links';
4
+
5
+ describe('isEmptyLinks', () => {
6
+ it('is true when there is no primary link url', () => {
7
+ expect(isEmptyLinks(null)).toBe(true);
8
+ expect(isEmptyLinks({ primaryLinkUrl: '' })).toBe(true);
9
+ });
10
+
11
+ it('is false when a primary link url is present', () => {
12
+ expect(isEmptyLinks({ primaryLinkUrl: 'https://acme.com' })).toBe(false);
13
+ });
14
+ });
@@ -0,0 +1,14 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { isEmptyPhones } from 'src/logic-functions/utils/is-empty-phones';
4
+
5
+ describe('isEmptyPhones', () => {
6
+ it('is true when there is no primary phone number', () => {
7
+ expect(isEmptyPhones(null)).toBe(true);
8
+ expect(isEmptyPhones({ primaryPhoneNumber: '' })).toBe(true);
9
+ });
10
+
11
+ it('is false when a primary phone number is present', () => {
12
+ expect(isEmptyPhones({ primaryPhoneNumber: '5551234' })).toBe(false);
13
+ });
14
+ });
@@ -0,0 +1,15 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { isEmptyText } from 'src/logic-functions/utils/is-empty-text';
4
+
5
+ describe('isEmptyText', () => {
6
+ it('is true for empty, whitespace and non-string values', () => {
7
+ expect(isEmptyText('')).toBe(true);
8
+ expect(isEmptyText(' ')).toBe(true);
9
+ expect(isEmptyText(null)).toBe(true);
10
+ });
11
+
12
+ it('is false for a non-empty string', () => {
13
+ expect(isEmptyText('hello')).toBe(false);
14
+ });
15
+ });
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { PDL_COMPANY_DATA_MOCK } from 'src/logic-functions/__mocks__/pdl-company-data.mock';
4
+ import { mapCompany } from 'src/logic-functions/utils/map-company';
5
+
6
+ describe('mapCompany', () => {
7
+ it('maps standard fields including the address composite', () => {
8
+ const { standard } = mapCompany(PDL_COMPANY_DATA_MOCK);
9
+
10
+ expect(standard.name).toBe('Acme Corp');
11
+ expect(standard.domainName).toMatchObject({ primaryLinkUrl: 'acme.com' });
12
+ expect(standard.address).toMatchObject({
13
+ addressCity: 'San Francisco',
14
+ addressPostcode: '94107',
15
+ addressCountry: 'United States',
16
+ });
17
+ });
18
+
19
+ it('converts USD funding to currency micros', () => {
20
+ const { pdl } = mapCompany(PDL_COMPANY_DATA_MOCK);
21
+
22
+ expect(pdl.pdlTotalFunding).toEqual({
23
+ amountMicros: 1_234_500_000,
24
+ currencyCode: 'USD',
25
+ });
26
+ });
27
+
28
+ it('maps select and multi-select funding fields, dropping unknowns', () => {
29
+ const { pdl } = mapCompany(PDL_COMPANY_DATA_MOCK);
30
+
31
+ expect(pdl.pdlSizeRange).toBe('FIFTY_ONE_TO_TWO_HUNDRED');
32
+ expect(pdl.pdlCompanyType).toBe('PRIVATE');
33
+ expect(pdl.pdlIndustry).toBe('ACCOUNTING');
34
+ expect(pdl.pdlLocationContinent).toBe('NORTH_AMERICA');
35
+ expect(pdl.pdlLatestFundingStage).toBe('SERIES_A');
36
+ expect(pdl.pdlFundingStages).toEqual(['SEED', 'SERIES_A']);
37
+ });
38
+
39
+ it('maps numbers, dates, arrays and raw json', () => {
40
+ const { pdl } = mapCompany(PDL_COMPANY_DATA_MOCK);
41
+
42
+ expect(pdl.pdlEmployeeCount).toBe(120);
43
+ expect(pdl.pdlFoundedYear).toBe(2001);
44
+ expect(pdl.pdlLastFundingDate).toBe('2020-05-01');
45
+ expect(pdl.pdlTags).toEqual(['saas', 'b2b']);
46
+ expect(pdl.pdlNaics).toEqual([{ naics_code: '541511' }]);
47
+ });
48
+ });