@twentyhq/call-recorder 1.0.1 → 1.0.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 (237) hide show
  1. package/manifest.json +297 -0
  2. package/package.json +2 -2
  3. package/src/front-components/calendar-event-recording.front-component.mjs +269 -0
  4. package/src/front-components/calendar-event-recording.front-component.mjs.map +7 -0
  5. package/src/logic-functions/process-recall-webhook.mjs +1744 -0
  6. package/src/logic-functions/process-recall-webhook.mjs.map +7 -0
  7. package/src/logic-functions/recall-webhook.mjs +391 -0
  8. package/src/logic-functions/recall-webhook.mjs.map +7 -0
  9. package/src/logic-functions/reconcile-call-recorder-calendar-event.mjs +1602 -0
  10. package/src/logic-functions/reconcile-call-recorder-calendar-event.mjs.map +7 -0
  11. package/src/logic-functions/reconcile-stale-bot-state.mjs +2268 -0
  12. package/src/logic-functions/reconcile-stale-bot-state.mjs.map +7 -0
  13. package/.env.example +0 -5
  14. package/.nvmrc +0 -1
  15. package/.oxlintrc.json +0 -20
  16. package/AGENTS.md +0 -67
  17. package/CLAUDE.md +0 -67
  18. package/README.md +0 -24
  19. package/SETUP.md +0 -95
  20. package/src/__tests__/global-setup.ts +0 -100
  21. package/src/__tests__/schema.integration-test.ts +0 -104
  22. package/src/application-config.ts +0 -96
  23. package/src/constants/__tests__/call-recording-field-universal-identifiers.test.ts +0 -19
  24. package/src/constants/app-description.ts +0 -2
  25. package/src/constants/app-display-name.ts +0 -1
  26. package/src/constants/application-universal-identifier.ts +0 -2
  27. package/src/constants/calendar-event-reconciliation-logic-function-universal-identifier.ts +0 -2
  28. package/src/constants/calendar-event-record-page-layout-universal-identifier.ts +0 -2
  29. package/src/constants/calendar-event-recording-front-component-universal-identifier.ts +0 -2
  30. package/src/constants/calendar-event-recording-page-layout-tab-universal-identifier.ts +0 -2
  31. package/src/constants/calendar-event-recording-page-layout-widget-universal-identifier.ts +0 -2
  32. package/src/constants/call-recorder-everyone-left-timeout-seconds-app-variable-universal-identifier.ts +0 -2
  33. package/src/constants/call-recorder-failure-reason-on-call-recording-field-universal-identifier.ts +0 -2
  34. package/src/constants/call-recorder-join-early-minutes-app-variable-universal-identifier.ts +0 -2
  35. package/src/constants/call-recorder-name-app-variable-universal-identifier.ts +0 -2
  36. package/src/constants/call-recorder-noone-joined-timeout-seconds-app-variable-universal-identifier.ts +0 -2
  37. package/src/constants/call-recorder-preference-off-option-id.ts +0 -2
  38. package/src/constants/call-recorder-preference-on-calendar-event-field-universal-identifier.ts +0 -2
  39. package/src/constants/call-recorder-preference-on-calendar-event-view-field-universal-identifier.ts +0 -2
  40. package/src/constants/call-recorder-preference-on-option-id.ts +0 -2
  41. package/src/constants/call-recorder-preference.ts +0 -4
  42. package/src/constants/call-recorder-waiting-room-timeout-seconds-app-variable-universal-identifier.ts +0 -2
  43. package/src/constants/call-recording-audio-field-universal-identifier.ts +0 -2
  44. package/src/constants/call-recording-video-field-universal-identifier.ts +0 -2
  45. package/src/constants/default-role-universal-identifier.ts +0 -2
  46. package/src/constants/process-recall-webhook-logic-function-universal-identifier.ts +0 -2
  47. package/src/constants/recall-webhook-logic-function-universal-identifier.ts +0 -2
  48. package/src/constants/stale-bot-state-logic-function-universal-identifier.ts +0 -2
  49. package/src/default-role.ts +0 -69
  50. package/src/fields/call-recorder-failure-reason-on-call-recording.field.ts +0 -22
  51. package/src/fields/call-recorder-preference-on-calendar-event.field.ts +0 -41
  52. package/src/front-components/calendar-event-recording.front-component.tsx +0 -13
  53. package/src/front-components/components/CalendarEventRecording.tsx +0 -39
  54. package/src/front-components/components/CalendarEventRecordingBody.tsx +0 -96
  55. package/src/front-components/components/CalendarEventRecordingContent.tsx +0 -111
  56. package/src/front-components/components/RecordingTranscript.tsx +0 -92
  57. package/src/front-components/components/RecordingVideoPlayer.tsx +0 -52
  58. package/src/front-components/components/TranscriptEntryList.tsx +0 -61
  59. package/src/front-components/components/TranscriptEntryListItem.tsx +0 -115
  60. package/src/front-components/components/TranscriptErrorBox.tsx +0 -48
  61. package/src/front-components/components/TranscriptSpeakerAvatar.tsx +0 -141
  62. package/src/front-components/components/TranscriptSpeakerChip.tsx +0 -51
  63. package/src/front-components/constants/recording-theme-css-variables.ts +0 -40
  64. package/src/front-components/hooks/use-calendar-event-participants.ts +0 -172
  65. package/src/front-components/hooks/use-calendar-event-recording.ts +0 -155
  66. package/src/front-components/types/calendar-event-participant-by-speaker-name.type.ts +0 -6
  67. package/src/front-components/types/calendar-event-recording-participant.type.ts +0 -7
  68. package/src/front-components/types/transcript-entry.type.ts +0 -13
  69. package/src/front-components/utils/__tests__/find-active-transcript-entry-index.test.ts +0 -66
  70. package/src/front-components/utils/__tests__/format-transcript-timestamp.test.ts +0 -29
  71. package/src/front-components/utils/__tests__/get-speaker-name-match-keys.test.ts +0 -22
  72. package/src/front-components/utils/__tests__/parse-transcript-entries.test.ts +0 -162
  73. package/src/front-components/utils/build-calendar-event-participant-by-speaker-name.util.ts +0 -45
  74. package/src/front-components/utils/find-active-transcript-entry-index.util.ts +0 -77
  75. package/src/front-components/utils/format-transcript-timestamp.util.ts +0 -16
  76. package/src/front-components/utils/get-absolute-avatar-url.util.ts +0 -48
  77. package/src/front-components/utils/get-calendar-event-participant-for-speaker-name.util.ts +0 -24
  78. package/src/front-components/utils/get-speaker-name-match-keys.util.ts +0 -64
  79. package/src/front-components/utils/get-video-file-extension.util.ts +0 -23
  80. package/src/front-components/utils/parse-transcript-entries.util.ts +0 -85
  81. package/src/logic-functions/__tests__/process-recall-webhook.test.ts +0 -62
  82. package/src/logic-functions/__tests__/recall-webhook.test.ts +0 -180
  83. package/src/logic-functions/constants/call-recorder-everyone-left-timeout-seconds-env-var-name.ts +0 -2
  84. package/src/logic-functions/constants/call-recorder-everyone-left-timeout-seconds.ts +0 -1
  85. package/src/logic-functions/constants/call-recorder-join-early-minutes-env-var-name.ts +0 -2
  86. package/src/logic-functions/constants/call-recorder-name-env-var-name.ts +0 -1
  87. package/src/logic-functions/constants/call-recorder-noone-joined-timeout-seconds-env-var-name.ts +0 -2
  88. package/src/logic-functions/constants/call-recorder-noone-joined-timeout-seconds.ts +0 -1
  89. package/src/logic-functions/constants/call-recorder-recording-retention-hours-env-var-name.ts +0 -2
  90. package/src/logic-functions/constants/call-recorder-waiting-room-timeout-seconds-env-var-name.ts +0 -2
  91. package/src/logic-functions/constants/call-recorder-waiting-room-timeout-seconds.ts +0 -1
  92. package/src/logic-functions/constants/call-recording-micro-credits-per-hour.ts +0 -1
  93. package/src/logic-functions/constants/call-recording-request-status.ts +0 -5
  94. package/src/logic-functions/constants/call-recording-status.ts +0 -9
  95. package/src/logic-functions/constants/default-call-recorder-join-early-minutes.ts +0 -1
  96. package/src/logic-functions/constants/default-call-recorder-name.ts +0 -1
  97. package/src/logic-functions/constants/default-call-recorder-recording-retention-hours.ts +0 -2
  98. package/src/logic-functions/constants/default-recall-region.ts +0 -1
  99. package/src/logic-functions/constants/milliseconds-per-minute.ts +0 -1
  100. package/src/logic-functions/constants/non-terminal-call-recording-statuses.ts +0 -8
  101. package/src/logic-functions/constants/recall-api-key-env-var-name.ts +0 -1
  102. package/src/logic-functions/constants/recall-api-max-attempts.ts +0 -1
  103. package/src/logic-functions/constants/recall-api-retry-delay-ms.ts +0 -1
  104. package/src/logic-functions/constants/recall-bot-automatic-leave.ts +0 -74
  105. package/src/logic-functions/constants/recall-bot-everyone-left-min-activate-after-seconds.ts +0 -1
  106. package/src/logic-functions/constants/recall-bot-recording-config.ts +0 -34
  107. package/src/logic-functions/constants/recall-region-env-var-name.ts +0 -1
  108. package/src/logic-functions/constants/recall-webhook-secret-env-var-name.ts +0 -1
  109. package/src/logic-functions/constants/restricted-field-placeholder.ts +0 -3
  110. package/src/logic-functions/constants/stale-bot-state-cron-pattern.ts +0 -1
  111. package/src/logic-functions/constants/twenty-page-size.ts +0 -1
  112. package/src/logic-functions/data/__tests__/complete-call-recording-ingestion.test.ts +0 -55
  113. package/src/logic-functions/data/__tests__/fetch-all-nodes.test.ts +0 -43
  114. package/src/logic-functions/data/__tests__/get-current-workspace-id.test.ts +0 -38
  115. package/src/logic-functions/data/__tests__/strip-restricted-field-value.test.ts +0 -22
  116. package/src/logic-functions/data/complete-call-recording-ingestion.util.ts +0 -24
  117. package/src/logic-functions/data/create-call-recording.util.ts +0 -41
  118. package/src/logic-functions/data/fetch-all-nodes.util.ts +0 -44
  119. package/src/logic-functions/data/fetch-calendar-events-by-filter.util.ts +0 -80
  120. package/src/logic-functions/data/fetch-calendar-events-by-ids.util.ts +0 -20
  121. package/src/logic-functions/data/fetch-calendar-events-by-starts-at-values.util.ts +0 -19
  122. package/src/logic-functions/data/find-call-recordings-by-calendar-event-ids.util.ts +0 -17
  123. package/src/logic-functions/data/find-call-recordings-by-filter.util.ts +0 -102
  124. package/src/logic-functions/data/find-call-recordings-by-ids.util.ts +0 -17
  125. package/src/logic-functions/data/find-open-scheduled-call-recordings.util.ts +0 -14
  126. package/src/logic-functions/data/get-current-workspace-id.util.ts +0 -36
  127. package/src/logic-functions/data/strip-restricted-field-value.util.ts +0 -6
  128. package/src/logic-functions/data/update-call-recording.util.ts +0 -24
  129. package/src/logic-functions/domain/__tests__/build-call-recorder-policy-result.test.ts +0 -47
  130. package/src/logic-functions/domain/__tests__/compute-call-recording-charge.test.ts +0 -71
  131. package/src/logic-functions/domain/__tests__/compute-call-recording-id-for-meeting.test.ts +0 -37
  132. package/src/logic-functions/domain/__tests__/compute-real-meeting-key.test.ts +0 -88
  133. package/src/logic-functions/domain/__tests__/is-call-recording-ingestion-complete.test.ts +0 -59
  134. package/src/logic-functions/domain/__tests__/is-call-recording-status-downgrade.test.ts +0 -37
  135. package/src/logic-functions/domain/__tests__/resolve-call-recorder-policy-result.test.ts +0 -120
  136. package/src/logic-functions/domain/__tests__/should-complete-call-recording-ingestion.test.ts +0 -102
  137. package/src/logic-functions/domain/aggregate-call-recorder-policy-results-by-meeting.util.ts +0 -42
  138. package/src/logic-functions/domain/build-call-recorder-policy-result.util.ts +0 -53
  139. package/src/logic-functions/domain/build-failed-transcript-marker.util.ts +0 -13
  140. package/src/logic-functions/domain/build-pending-transcript-marker.util.ts +0 -13
  141. package/src/logic-functions/domain/build-recall-routing-metadata.util.ts +0 -12
  142. package/src/logic-functions/domain/build-transcript-failure-reason.util.ts +0 -7
  143. package/src/logic-functions/domain/compute-call-recording-charge.util.ts +0 -41
  144. package/src/logic-functions/domain/compute-call-recording-id-for-meeting.util.ts +0 -16
  145. package/src/logic-functions/domain/compute-real-meeting-key.util.ts +0 -48
  146. package/src/logic-functions/domain/compute-recall-bot-join-at.util.ts +0 -34
  147. package/src/logic-functions/domain/is-call-recording-ingestion-complete.util.ts +0 -19
  148. package/src/logic-functions/domain/is-call-recording-status-downgrade.util.ts +0 -37
  149. package/src/logic-functions/domain/is-recall-recording-done-signal.util.ts +0 -13
  150. package/src/logic-functions/domain/map-recall-status-code-to-call-recording-status.util.ts +0 -26
  151. package/src/logic-functions/domain/parse-transcript-marker.util.ts +0 -29
  152. package/src/logic-functions/domain/resolve-call-recorder-policy-result.util.ts +0 -72
  153. package/src/logic-functions/domain/should-complete-call-recording-ingestion.util.ts +0 -32
  154. package/src/logic-functions/flows/__tests__/charge-completed-call-recording.test.ts +0 -45
  155. package/src/logic-functions/flows/__tests__/complete-and-charge-call-recording.test.ts +0 -61
  156. package/src/logic-functions/flows/__tests__/converge-diverged-call-recordings.test.ts +0 -727
  157. package/src/logic-functions/flows/__tests__/download-transcript.test.ts +0 -74
  158. package/src/logic-functions/flows/__tests__/handle-recall-webhook.test.ts +0 -1301
  159. package/src/logic-functions/flows/__tests__/heal-call-recordings-missing-bot.test.ts +0 -225
  160. package/src/logic-functions/flows/__tests__/ingest-call-recording-media.test.ts +0 -153
  161. package/src/logic-functions/flows/__tests__/reap-orphaned-call-recorders.test.ts +0 -425
  162. package/src/logic-functions/flows/__tests__/reconcile-call-recorder.test.ts +0 -1007
  163. package/src/logic-functions/flows/cancel-call-recording-request.util.ts +0 -46
  164. package/src/logic-functions/flows/charge-completed-call-recording.util.ts +0 -31
  165. package/src/logic-functions/flows/complete-and-charge-call-recording.util.ts +0 -29
  166. package/src/logic-functions/flows/converge-diverged-call-recordings-result.type.ts +0 -8
  167. package/src/logic-functions/flows/converge-diverged-call-recordings.util.ts +0 -447
  168. package/src/logic-functions/flows/download-transcript.util.ts +0 -67
  169. package/src/logic-functions/flows/ensure-call-recorder.util.ts +0 -73
  170. package/src/logic-functions/flows/handle-recall-webhook.util.ts +0 -672
  171. package/src/logic-functions/flows/heal-call-recordings-missing-bot.util.ts +0 -82
  172. package/src/logic-functions/flows/ingest-call-recording-media.util.ts +0 -128
  173. package/src/logic-functions/flows/persist-call-recording-progress.util.ts +0 -58
  174. package/src/logic-functions/flows/reap-orphaned-call-recorders.util.ts +0 -183
  175. package/src/logic-functions/flows/reconcile-call-recorder.util.ts +0 -495
  176. package/src/logic-functions/flows/reconcile-call-recording-transcript-artifact-result.type.ts +0 -11
  177. package/src/logic-functions/flows/reconcile-call-recording-transcript-artifact.util.ts +0 -182
  178. package/src/logic-functions/flows/reschedule-call-recording-bot.util.ts +0 -69
  179. package/src/logic-functions/process-recall-webhook.ts +0 -23
  180. package/src/logic-functions/recall-api/__tests__/extract-recall-bot-convergence.test.ts +0 -153
  181. package/src/logic-functions/recall-api/__tests__/extract-recall-media-urls.test.ts +0 -67
  182. package/src/logic-functions/recall-api/__tests__/recall-bot-api.test.ts +0 -744
  183. package/src/logic-functions/recall-api/__tests__/verify-recall-webhook-signature.test.ts +0 -122
  184. package/src/logic-functions/recall-api/cancel-recall-bot.util.ts +0 -28
  185. package/src/logic-functions/recall-api/create-async-recall-transcript.util.ts +0 -47
  186. package/src/logic-functions/recall-api/eject-recall-bot.util.ts +0 -28
  187. package/src/logic-functions/recall-api/extract-recall-bot-convergence.util.ts +0 -149
  188. package/src/logic-functions/recall-api/extract-recall-bot-id.util.ts +0 -10
  189. package/src/logic-functions/recall-api/extract-recall-media-urls.util.ts +0 -30
  190. package/src/logic-functions/recall-api/extract-twenty-workspace-id-from-recall-webhook.util.ts +0 -8
  191. package/src/logic-functions/recall-api/get-recall-api-config.util.ts +0 -59
  192. package/src/logic-functions/recall-api/get-recall-bot.util.ts +0 -42
  193. package/src/logic-functions/recall-api/get-recall-recording.util.ts +0 -31
  194. package/src/logic-functions/recall-api/get-recall-webhook-bot-metadata.util.ts +0 -18
  195. package/src/logic-functions/recall-api/list-recall-transcripts.util.ts +0 -141
  196. package/src/logic-functions/recall-api/list-scheduled-recall-bots.util.ts +0 -106
  197. package/src/logic-functions/recall-api/normalize-recall-timestamp.util.ts +0 -14
  198. package/src/logic-functions/recall-api/parse-recall-webhook-event.util.ts +0 -88
  199. package/src/logic-functions/recall-api/recall-bot-api-request.util.ts +0 -165
  200. package/src/logic-functions/recall-api/recall-transcript-summary.type.ts +0 -5
  201. package/src/logic-functions/recall-api/reschedule-recall-bot.util.ts +0 -56
  202. package/src/logic-functions/recall-api/retrieve-recall-transcript.util.ts +0 -71
  203. package/src/logic-functions/recall-api/schedule-recall-bot.util.ts +0 -68
  204. package/src/logic-functions/recall-api/verify-recall-webhook-signature.util.ts +0 -109
  205. package/src/logic-functions/recall-webhook.ts +0 -90
  206. package/src/logic-functions/reconcile-call-recorder-calendar-event.ts +0 -178
  207. package/src/logic-functions/reconcile-stale-bot-state.ts +0 -106
  208. package/src/logic-functions/types/calendar-event-record.type.ts +0 -5
  209. package/src/logic-functions/types/call-recorder-policy-calendar-event-input.type.ts +0 -10
  210. package/src/logic-functions/types/call-recorder-policy-input.type.ts +0 -9
  211. package/src/logic-functions/types/call-recorder-policy-not-required-reason.type.ts +0 -5
  212. package/src/logic-functions/types/call-recorder-policy-required-reason.type.ts +0 -1
  213. package/src/logic-functions/types/call-recorder-policy-result-for-calendar-event.type.ts +0 -9
  214. package/src/logic-functions/types/call-recorder-policy-result-for-meeting.type.ts +0 -6
  215. package/src/logic-functions/types/call-recorder-policy-result.type.ts +0 -12
  216. package/src/logic-functions/types/call-recorder-reconciliation-result.type.ts +0 -16
  217. package/src/logic-functions/types/call-recording-media-file.type.ts +0 -1
  218. package/src/logic-functions/types/call-recording-record.type.ts +0 -15
  219. package/src/logic-functions/types/call-recording-update-fields.type.ts +0 -20
  220. package/src/logic-functions/types/files-field-value.type.ts +0 -1
  221. package/src/logic-functions/types/meeting-recording.type.ts +0 -7
  222. package/src/logic-functions/types/recall-bot-operation-result.type.ts +0 -19
  223. package/src/logic-functions/types/recall-routing-metadata.type.ts +0 -4
  224. package/src/logic-functions/types/removed-call-recorder-occurrence.type.ts +0 -6
  225. package/src/logic-functions/types/transcript-marker.type.ts +0 -6
  226. package/src/logic-functions/utils/as-record.util.ts +0 -6
  227. package/src/logic-functions/utils/get-application-variable-value.util.ts +0 -3
  228. package/src/logic-functions/utils/get-record-at-path.util.ts +0 -10
  229. package/src/logic-functions/utils/get-string.util.ts +0 -4
  230. package/src/logic-functions/utils/get-unique-sorted-ids.util.ts +0 -8
  231. package/src/logic-functions/utils/is-non-empty-string.util.ts +0 -5
  232. package/src/page-layouts/calendar-event-recording-tab.ts +0 -33
  233. package/src/view-fields/call-recorder-preference-on-calendar-event.view-field.ts +0 -27
  234. package/tsconfig.json +0 -42
  235. package/tsconfig.spec.json +0 -9
  236. package/vitest.config.ts +0 -31
  237. package/vitest.unit.config.ts +0 -14
@@ -1,44 +0,0 @@
1
- import { isString, isUndefined } from '@sniptt/guards';
2
-
3
- export type ConnectionPage<TNode> = {
4
- pageInfo?: {
5
- hasNextPage?: boolean | null;
6
- endCursor?: string | null;
7
- } | null;
8
- edges?: Array<{ node: TNode }> | null;
9
- };
10
-
11
- export const fetchAllNodes = async <TNode>(
12
- fetchPage: (
13
- afterCursor: string | undefined,
14
- ) => Promise<ConnectionPage<TNode> | undefined>,
15
- ): Promise<TNode[]> => {
16
- const nodes: TNode[] = [];
17
- let hasNextPage = true;
18
- let afterCursor: string | undefined;
19
-
20
- while (hasNextPage) {
21
- const connection = await fetchPage(afterCursor);
22
-
23
- if (isUndefined(connection)) {
24
- throw new Error('Pagination query returned no connection');
25
- }
26
-
27
- for (const edge of connection.edges ?? []) {
28
- nodes.push(edge.node);
29
- }
30
-
31
- hasNextPage = connection.pageInfo?.hasNextPage === true;
32
- const endCursor = connection.pageInfo?.endCursor;
33
-
34
- if (hasNextPage && !isString(endCursor)) {
35
- throw new Error(
36
- 'Inconsistent pagination state: hasNextPage is true without an endCursor',
37
- );
38
- }
39
-
40
- afterCursor = isString(endCursor) ? endCursor : undefined;
41
- }
42
-
43
- return nodes;
44
- };
@@ -1,80 +0,0 @@
1
- import { isString, isUndefined } from '@sniptt/guards';
2
- import { type CoreApiClient } from 'twenty-client-sdk/core';
3
-
4
- import { TWENTY_PAGE_SIZE } from 'src/logic-functions/constants/twenty-page-size';
5
- import { type CalendarEventRecord } from 'src/logic-functions/types/calendar-event-record.type';
6
- import {
7
- fetchAllNodes,
8
- type ConnectionPage,
9
- } from 'src/logic-functions/data/fetch-all-nodes.util';
10
- import { isNonEmptyString } from 'src/logic-functions/utils/is-non-empty-string.util';
11
- import { stripRestrictedFieldValue } from 'src/logic-functions/data/strip-restricted-field-value.util';
12
-
13
- type CalendarEventNode = {
14
- id: string;
15
- title?: string | null;
16
- isCanceled?: boolean | null;
17
- startsAt?: string | null;
18
- endsAt?: string | null;
19
- iCalUid?: string | null;
20
- conferenceLink?: { primaryLinkUrl?: string | null } | null;
21
- callRecorderPreference?: string | null;
22
- };
23
-
24
- export const fetchCalendarEventsByFilter = async (
25
- client: CoreApiClient,
26
- filter: Record<string, unknown>,
27
- ): Promise<CalendarEventRecord[]> => {
28
- const calendarEventNodes = await fetchAllNodes<CalendarEventNode>(
29
- async (afterCursor) => {
30
- const queryResult = await client.query({
31
- calendarEvents: {
32
- __args: {
33
- filter,
34
- first: TWENTY_PAGE_SIZE,
35
- ...(isUndefined(afterCursor) ? {} : { after: afterCursor }),
36
- },
37
- pageInfo: {
38
- hasNextPage: true,
39
- endCursor: true,
40
- },
41
- edges: {
42
- node: {
43
- id: true,
44
- title: true,
45
- isCanceled: true,
46
- startsAt: true,
47
- endsAt: true,
48
- iCalUid: true,
49
- conferenceLink: {
50
- primaryLinkUrl: true,
51
- },
52
- callRecorderPreference: true,
53
- },
54
- },
55
- },
56
- });
57
-
58
- return queryResult.calendarEvents as
59
- | ConnectionPage<CalendarEventNode>
60
- | undefined;
61
- },
62
- );
63
-
64
- return calendarEventNodes.map((calendarEvent) => ({
65
- id: calendarEvent.id,
66
- title: stripRestrictedFieldValue(calendarEvent.title ?? undefined),
67
- isCanceled: calendarEvent.isCanceled ?? false,
68
- startsAt: calendarEvent.startsAt ?? undefined,
69
- endsAt: calendarEvent.endsAt ?? undefined,
70
- iCalUid: calendarEvent.iCalUid ?? undefined,
71
- conferenceLinkUrl: isNonEmptyString(
72
- calendarEvent.conferenceLink?.primaryLinkUrl,
73
- )
74
- ? calendarEvent.conferenceLink.primaryLinkUrl
75
- : undefined,
76
- callRecorderPreference: isString(calendarEvent.callRecorderPreference)
77
- ? calendarEvent.callRecorderPreference
78
- : undefined,
79
- }));
80
- };
@@ -1,20 +0,0 @@
1
- import { type CoreApiClient } from 'twenty-client-sdk/core';
2
-
3
- import { type CalendarEventRecord } from 'src/logic-functions/types/calendar-event-record.type';
4
- import { fetchCalendarEventsByFilter } from 'src/logic-functions/data/fetch-calendar-events-by-filter.util';
5
- import { getUniqueSortedIds } from 'src/logic-functions/utils/get-unique-sorted-ids.util';
6
-
7
- export const fetchCalendarEventsByIds = async (
8
- client: CoreApiClient,
9
- calendarEventIds: string[],
10
- ): Promise<CalendarEventRecord[]> => {
11
- const uniqueCalendarEventIds = getUniqueSortedIds(calendarEventIds);
12
-
13
- if (uniqueCalendarEventIds.length === 0) {
14
- return [];
15
- }
16
-
17
- return fetchCalendarEventsByFilter(client, {
18
- id: { in: uniqueCalendarEventIds },
19
- });
20
- };
@@ -1,19 +0,0 @@
1
- import { type CoreApiClient } from 'twenty-client-sdk/core';
2
-
3
- import { type CalendarEventRecord } from 'src/logic-functions/types/calendar-event-record.type';
4
- import { fetchCalendarEventsByFilter } from 'src/logic-functions/data/fetch-calendar-events-by-filter.util';
5
-
6
- export const fetchCalendarEventsByStartsAtValues = async (
7
- client: CoreApiClient,
8
- startsAtValues: string[],
9
- ): Promise<CalendarEventRecord[]> => {
10
- const uniqueStartsAtValues = [...new Set(startsAtValues)].sort();
11
-
12
- if (uniqueStartsAtValues.length === 0) {
13
- return [];
14
- }
15
-
16
- return fetchCalendarEventsByFilter(client, {
17
- startsAt: { in: uniqueStartsAtValues },
18
- });
19
- };
@@ -1,17 +0,0 @@
1
- import { type CoreApiClient } from 'twenty-client-sdk/core';
2
-
3
- import { type CallRecordingRecord } from 'src/logic-functions/types/call-recording-record.type';
4
- import { findCallRecordingsByFilter } from 'src/logic-functions/data/find-call-recordings-by-filter.util';
5
-
6
- export const findCallRecordingsByCalendarEventIds = async (
7
- client: CoreApiClient,
8
- calendarEventIds: string[],
9
- ): Promise<CallRecordingRecord[]> => {
10
- if (calendarEventIds.length === 0) {
11
- return [];
12
- }
13
-
14
- return findCallRecordingsByFilter(client, {
15
- calendarEventId: { in: calendarEventIds },
16
- });
17
- };
@@ -1,102 +0,0 @@
1
- import { isUndefined } from '@sniptt/guards';
2
- import { type CoreApiClient } from 'twenty-client-sdk/core';
3
-
4
- import { CallRecordingRequestStatus } from 'src/logic-functions/constants/call-recording-request-status';
5
- import { TWENTY_PAGE_SIZE } from 'src/logic-functions/constants/twenty-page-size';
6
- import { type CallRecordingRecord } from 'src/logic-functions/types/call-recording-record.type';
7
- import {
8
- fetchAllNodes,
9
- type ConnectionPage,
10
- } from 'src/logic-functions/data/fetch-all-nodes.util';
11
- import { isNonEmptyString } from 'src/logic-functions/utils/is-non-empty-string.util';
12
-
13
- type CallRecordingNode = {
14
- id: string;
15
- title?: string | null;
16
- status?: string | null;
17
- recordingRequestStatus?: unknown;
18
- startedAt?: string | null;
19
- endedAt?: string | null;
20
- calendarEventId?: string | null;
21
- externalBotId?: string | null;
22
- externalRecordingId?: string | null;
23
- callRecorderFailureReason?: string | null;
24
- };
25
-
26
- export const findCallRecordingsByFilter = async (
27
- client: CoreApiClient,
28
- filter: Record<string, unknown>,
29
- ): Promise<CallRecordingRecord[]> => {
30
- const callRecordingNodes = await fetchAllNodes<CallRecordingNode>(
31
- async (afterCursor) => {
32
- const queryResult = await client.query({
33
- callRecordings: {
34
- __args: {
35
- filter,
36
- first: TWENTY_PAGE_SIZE,
37
- ...(isUndefined(afterCursor) ? {} : { after: afterCursor }),
38
- },
39
- pageInfo: {
40
- hasNextPage: true,
41
- endCursor: true,
42
- },
43
- edges: {
44
- node: {
45
- id: true,
46
- title: true,
47
- status: true,
48
- recordingRequestStatus: true,
49
- startedAt: true,
50
- endedAt: true,
51
- calendarEventId: true,
52
- externalBotId: true,
53
- externalRecordingId: true,
54
- callRecorderFailureReason: true,
55
- },
56
- },
57
- },
58
- });
59
-
60
- return queryResult.callRecordings as
61
- | ConnectionPage<CallRecordingNode>
62
- | undefined;
63
- },
64
- );
65
-
66
- return callRecordingNodes.map((callRecording) => ({
67
- id: callRecording.id,
68
- title: callRecording.title ?? undefined,
69
- status: callRecording.status ?? undefined,
70
- recordingRequestStatus: normalizeCallRecordingRequestStatus(
71
- callRecording.recordingRequestStatus,
72
- ),
73
- startedAt: callRecording.startedAt ?? undefined,
74
- endedAt: callRecording.endedAt ?? undefined,
75
- calendarEventId: callRecording.calendarEventId ?? undefined,
76
- externalBotId: normalizeOptionalString(callRecording.externalBotId),
77
- externalRecordingId: normalizeOptionalString(
78
- callRecording.externalRecordingId,
79
- ),
80
- callRecorderFailureReason: normalizeOptionalString(
81
- callRecording.callRecorderFailureReason,
82
- ),
83
- }));
84
- };
85
-
86
- const normalizeOptionalString = (
87
- value: string | null | undefined,
88
- ): string | undefined => (isNonEmptyString(value) ? value : undefined);
89
-
90
- const normalizeCallRecordingRequestStatus = (
91
- recordingRequestStatus: unknown,
92
- ): CallRecordingRequestStatus | undefined => {
93
- if (recordingRequestStatus === CallRecordingRequestStatus.REQUESTED) {
94
- return recordingRequestStatus;
95
- }
96
-
97
- if (recordingRequestStatus === CallRecordingRequestStatus.CANCELED) {
98
- return recordingRequestStatus;
99
- }
100
-
101
- return undefined;
102
- };
@@ -1,17 +0,0 @@
1
- import { type CoreApiClient } from 'twenty-client-sdk/core';
2
-
3
- import { type CallRecordingRecord } from 'src/logic-functions/types/call-recording-record.type';
4
- import { findCallRecordingsByFilter } from 'src/logic-functions/data/find-call-recordings-by-filter.util';
5
-
6
- export const findCallRecordingsByIds = async (
7
- client: CoreApiClient,
8
- callRecordingIds: string[],
9
- ): Promise<CallRecordingRecord[]> => {
10
- if (callRecordingIds.length === 0) {
11
- return [];
12
- }
13
-
14
- return findCallRecordingsByFilter(client, {
15
- id: { in: callRecordingIds },
16
- });
17
- };
@@ -1,14 +0,0 @@
1
- import { type CoreApiClient } from 'twenty-client-sdk/core';
2
-
3
- import { CallRecordingRequestStatus } from 'src/logic-functions/constants/call-recording-request-status';
4
- import { CallRecordingStatus } from 'src/logic-functions/constants/call-recording-status';
5
- import { type CallRecordingRecord } from 'src/logic-functions/types/call-recording-record.type';
6
- import { findCallRecordingsByFilter } from 'src/logic-functions/data/find-call-recordings-by-filter.util';
7
-
8
- export const findOpenScheduledCallRecordings = async (
9
- client: CoreApiClient,
10
- ): Promise<CallRecordingRecord[]> =>
11
- findCallRecordingsByFilter(client, {
12
- recordingRequestStatus: { eq: CallRecordingRequestStatus.REQUESTED },
13
- status: { eq: CallRecordingStatus.SCHEDULED },
14
- });
@@ -1,36 +0,0 @@
1
- import { isUndefined } from '@sniptt/guards';
2
-
3
- import { asRecord } from 'src/logic-functions/utils/as-record.util';
4
- import { getString } from 'src/logic-functions/utils/get-string.util';
5
-
6
- const APP_ACCESS_TOKEN_ENV_VAR_NAME = 'TWENTY_APP_ACCESS_TOKEN';
7
-
8
- export const getCurrentWorkspaceId = (): string | undefined => {
9
- const accessToken = getString(process.env[APP_ACCESS_TOKEN_ENV_VAR_NAME]);
10
-
11
- if (isUndefined(accessToken)) {
12
- return undefined;
13
- }
14
-
15
- return getWorkspaceIdFromAccessToken(accessToken);
16
- };
17
-
18
- const getWorkspaceIdFromAccessToken = (
19
- accessToken: string,
20
- ): string | undefined => {
21
- const encodedPayload = accessToken.split('.')[1];
22
-
23
- if (isUndefined(encodedPayload)) {
24
- return undefined;
25
- }
26
-
27
- try {
28
- const payload = asRecord(
29
- JSON.parse(Buffer.from(encodedPayload, 'base64url').toString('utf8')),
30
- );
31
-
32
- return getString(payload?.workspaceId);
33
- } catch {
34
- return undefined;
35
- }
36
- };
@@ -1,6 +0,0 @@
1
- import { RESTRICTED_FIELD_PLACEHOLDER } from 'src/logic-functions/constants/restricted-field-placeholder';
2
-
3
- export const stripRestrictedFieldValue = (
4
- value: string | undefined,
5
- ): string | undefined =>
6
- value === RESTRICTED_FIELD_PLACEHOLDER ? undefined : value;
@@ -1,24 +0,0 @@
1
- import { type CoreApiClient } from 'twenty-client-sdk/core';
2
-
3
- import { type CallRecordingUpdateFields } from 'src/logic-functions/types/call-recording-update-fields.type';
4
-
5
- export const updateCallRecording = async (
6
- client: CoreApiClient,
7
- {
8
- id,
9
- data,
10
- }: {
11
- id: string;
12
- data: CallRecordingUpdateFields;
13
- },
14
- ): Promise<void> => {
15
- await client.mutation({
16
- updateCallRecording: {
17
- __args: {
18
- id,
19
- data,
20
- },
21
- id: true,
22
- },
23
- });
24
- };
@@ -1,47 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { buildCallRecorderPolicyResult } from 'src/logic-functions/domain/build-call-recorder-policy-result.util';
4
- import { type CallRecorderPolicyCalendarEventInput } from 'src/logic-functions/types/call-recorder-policy-calendar-event-input.type';
5
-
6
- const NOW = new Date('2026-01-01T12:00:00.000Z');
7
-
8
- const buildCalendarEventInput = (
9
- overrides: Partial<CallRecorderPolicyCalendarEventInput>,
10
- ): CallRecorderPolicyCalendarEventInput => ({
11
- id: 'calendar-event-1',
12
- isCanceled: false,
13
- startsAt: '2026-01-01T13:00:00.000Z',
14
- endsAt: '2026-01-01T14:00:00.000Z',
15
- iCalUid: 'ical-uid-1',
16
- conferenceLinkUrl: 'https://meet.example.com/customer-sync',
17
- callRecorderPreference: undefined,
18
- ...overrides,
19
- });
20
-
21
- describe('buildCallRecorderPolicyResult', () => {
22
- it('requests a bot for the ON wire value', () => {
23
- const policyResult = buildCallRecorderPolicyResult(
24
- buildCalendarEventInput({
25
- callRecorderPreference: 'ON',
26
- }),
27
- NOW,
28
- );
29
-
30
- expect(policyResult.callRecorderPreference).toBe('ON');
31
- expect(policyResult.shouldRequestBot).toBe(true);
32
- expect(policyResult.reason).toBe('RECORDING_ENABLED');
33
- });
34
-
35
- it('does not request a bot for the OFF wire value', () => {
36
- const policyResult = buildCallRecorderPolicyResult(
37
- buildCalendarEventInput({
38
- callRecorderPreference: 'OFF',
39
- }),
40
- NOW,
41
- );
42
-
43
- expect(policyResult.callRecorderPreference).toBe('OFF');
44
- expect(policyResult.shouldRequestBot).toBe(false);
45
- expect(policyResult.reason).toBe('PREFERENCE_OFF');
46
- });
47
- });
@@ -1,71 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { computeCallRecordingCharge } from 'src/logic-functions/domain/compute-call-recording-charge.util';
4
-
5
- describe('computeCallRecordingCharge', () => {
6
- it('charges one credit for a one-hour recording', () => {
7
- expect(
8
- computeCallRecordingCharge({
9
- startedAt: '2026-06-10T09:00:00.000Z',
10
- endedAt: '2026-06-10T10:00:00.000Z',
11
- }),
12
- ).toEqual({
13
- creditsUsedMicro: 1_000_000,
14
- quantityMinutes: 60,
15
- });
16
- });
17
-
18
- it('prorates partial hours by duration', () => {
19
- expect(
20
- computeCallRecordingCharge({
21
- startedAt: '2026-06-10T09:00:00.000Z',
22
- endedAt: '2026-06-10T09:45:00.000Z',
23
- }),
24
- ).toEqual({
25
- creditsUsedMicro: 750_000,
26
- quantityMinutes: 45,
27
- });
28
- });
29
-
30
- it('reports at least one minute for very short recordings', () => {
31
- expect(
32
- computeCallRecordingCharge({
33
- startedAt: '2026-06-10T09:00:00.000Z',
34
- endedAt: '2026-06-10T09:00:30.000Z',
35
- }),
36
- ).toEqual({
37
- creditsUsedMicro: 8_333,
38
- quantityMinutes: 1,
39
- });
40
- });
41
-
42
- it('returns undefined when either timestamp is missing', () => {
43
- expect(
44
- computeCallRecordingCharge({
45
- startedAt: undefined,
46
- endedAt: '2026-06-10T10:00:00.000Z',
47
- }),
48
- ).toBeUndefined();
49
- expect(
50
- computeCallRecordingCharge({
51
- startedAt: '2026-06-10T09:00:00.000Z',
52
- endedAt: undefined,
53
- }),
54
- ).toBeUndefined();
55
- });
56
-
57
- it('returns undefined for non-positive or unparseable durations', () => {
58
- expect(
59
- computeCallRecordingCharge({
60
- startedAt: '2026-06-10T10:00:00.000Z',
61
- endedAt: '2026-06-10T09:00:00.000Z',
62
- }),
63
- ).toBeUndefined();
64
- expect(
65
- computeCallRecordingCharge({
66
- startedAt: 'not-a-date',
67
- endedAt: '2026-06-10T10:00:00.000Z',
68
- }),
69
- ).toBeUndefined();
70
- });
71
- });
@@ -1,37 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { computeCallRecordingIdForMeeting } from 'src/logic-functions/domain/compute-call-recording-id-for-meeting.util';
4
-
5
- const UUID_V4_PATTERN =
6
- /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
7
-
8
- describe('computeCallRecordingIdForMeeting', () => {
9
- it('returns the same id for the same real meeting key', () => {
10
- const realMeetingKey =
11
- 'link:meet.example.com/sync:2026-01-01T13:00:00.000Z';
12
-
13
- expect(computeCallRecordingIdForMeeting(realMeetingKey)).toBe(
14
- computeCallRecordingIdForMeeting(realMeetingKey),
15
- );
16
- });
17
-
18
- it('returns different ids for different real meeting keys', () => {
19
- expect(
20
- computeCallRecordingIdForMeeting(
21
- 'link:meet.example.com/sync:2026-01-01T13:00:00.000Z',
22
- ),
23
- ).not.toBe(
24
- computeCallRecordingIdForMeeting(
25
- 'link:meet.example.com/sync:2026-01-02T13:00:00.000Z',
26
- ),
27
- );
28
- });
29
-
30
- it('returns a v4-shaped uuid', () => {
31
- expect(
32
- computeCallRecordingIdForMeeting(
33
- 'ical:some-uid:2026-01-01T13:00:00.000Z',
34
- ),
35
- ).toMatch(UUID_V4_PATTERN);
36
- });
37
- });
@@ -1,88 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { computeRealMeetingKey } from 'src/logic-functions/domain/compute-real-meeting-key.util';
4
-
5
- const STARTS_AT = '2026-01-01T13:00:00.000Z';
6
-
7
- const buildInput = (
8
- overrides: Partial<Parameters<typeof computeRealMeetingKey>[0]> = {},
9
- ) => ({
10
- calendarEventId: 'calendar-event-1',
11
- conferenceLinkUrl: 'https://meet.example.com/customer-sync',
12
- iCalUid: 'calendar-event-uid',
13
- startsAt: STARTS_AT,
14
- ...overrides,
15
- });
16
-
17
- describe('computeRealMeetingKey', () => {
18
- it.each([
19
- [
20
- 'strips protocol, query, and fragment',
21
- 'https://zoom.us/j/123?pwd=abc#section',
22
- `link:zoom.us/j/123:${STARTS_AT}`,
23
- ],
24
- [
25
- 'strips www and lowercases',
26
- 'HTTPS://WWW.Meet.Example.com/Customer-Sync',
27
- `link:meet.example.com/customer-sync:${STARTS_AT}`,
28
- ],
29
- [
30
- 'strips trailing slashes',
31
- 'https://meet.example.com/customer-sync///',
32
- `link:meet.example.com/customer-sync:${STARTS_AT}`,
33
- ],
34
- [
35
- 'supports plain http links',
36
- 'http://meet.example.com/customer-sync',
37
- `link:meet.example.com/customer-sync:${STARTS_AT}`,
38
- ],
39
- ])('%s', (_label, conferenceLinkUrl, expectedKey) => {
40
- expect(computeRealMeetingKey(buildInput({ conferenceLinkUrl }))).toBe(
41
- expectedKey,
42
- );
43
- });
44
-
45
- it('produces the same key for the same meeting synced from two calendars', () => {
46
- const fromFirstAttendee = computeRealMeetingKey(
47
- buildInput({
48
- calendarEventId: 'calendar-event-1',
49
- conferenceLinkUrl: 'https://zoom.us/j/123?pwd=first-attendee-token',
50
- }),
51
- );
52
- const fromSecondAttendee = computeRealMeetingKey(
53
- buildInput({
54
- calendarEventId: 'calendar-event-2',
55
- conferenceLinkUrl:
56
- 'https://www.zoom.us/j/123?pwd=second-attendee-token',
57
- }),
58
- );
59
-
60
- expect(fromFirstAttendee).toBe(fromSecondAttendee);
61
- });
62
-
63
- it('falls back to the iCal uid when the link is blank', () => {
64
- expect(
65
- computeRealMeetingKey(buildInput({ conferenceLinkUrl: ' ' })),
66
- ).toBe(`ical:calendar-event-uid:${STARTS_AT}`);
67
- });
68
-
69
- it('falls back to the iCal uid when the link is not a string', () => {
70
- expect(computeRealMeetingKey(buildInput({ conferenceLinkUrl: 42 }))).toBe(
71
- `ical:calendar-event-uid:${STARTS_AT}`,
72
- );
73
- });
74
-
75
- it('falls back to the calendar event id when link and iCal uid are missing', () => {
76
- expect(
77
- computeRealMeetingKey(
78
- buildInput({ conferenceLinkUrl: undefined, iCalUid: '' }),
79
- ),
80
- ).toBe('event:calendar-event-1');
81
- });
82
-
83
- it('keeps link keys distinct across start times', () => {
84
- expect(computeRealMeetingKey(buildInput({ startsAt: undefined }))).toBe(
85
- 'link:meet.example.com/customer-sync:',
86
- );
87
- });
88
- });