@memberjunction/server 6.2.0-edge.1 → 6.2.0-edge.3

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 (220) hide show
  1. package/README.md +30 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/config.d.ts +2189 -71
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +123 -3
  6. package/dist/config.js.map +1 -1
  7. package/dist/context.d.ts.map +1 -1
  8. package/dist/context.js +11 -23
  9. package/dist/context.js.map +1 -1
  10. package/dist/generated/graphql-schemas/__mj.d.ts +2143 -205
  11. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  12. package/dist/generated/graphql-schemas/__mj.js +24441 -14433
  13. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  14. package/dist/generic/FireAndForgetHeartbeat.d.ts +15 -1
  15. package/dist/generic/FireAndForgetHeartbeat.d.ts.map +1 -1
  16. package/dist/generic/FireAndForgetHeartbeat.js +16 -2
  17. package/dist/generic/FireAndForgetHeartbeat.js.map +1 -1
  18. package/dist/generic/OrphanedConversationDetailReconciler.d.ts +31 -0
  19. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -0
  20. package/dist/generic/OrphanedConversationDetailReconciler.js +215 -0
  21. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -0
  22. package/dist/generic/PushStatusResolver.d.ts +38 -0
  23. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  24. package/dist/generic/PushStatusResolver.js +84 -1
  25. package/dist/generic/PushStatusResolver.js.map +1 -1
  26. package/dist/generic/ResolverBase.d.ts +56 -4
  27. package/dist/generic/ResolverBase.d.ts.map +1 -1
  28. package/dist/generic/ResolverBase.js +191 -74
  29. package/dist/generic/ResolverBase.js.map +1 -1
  30. package/dist/generic/RunViewResolver.d.ts +4 -0
  31. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  32. package/dist/generic/RunViewResolver.js +33 -1
  33. package/dist/generic/RunViewResolver.js.map +1 -1
  34. package/dist/index.d.ts +4 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +204 -50
  37. package/dist/index.js.map +1 -1
  38. package/dist/postgresqlCompat.d.ts +8 -0
  39. package/dist/postgresqlCompat.d.ts.map +1 -0
  40. package/dist/postgresqlCompat.js +19 -0
  41. package/dist/postgresqlCompat.js.map +1 -0
  42. package/dist/postgresqlPoolSettings.d.ts +108 -0
  43. package/dist/postgresqlPoolSettings.d.ts.map +1 -0
  44. package/dist/postgresqlPoolSettings.js +134 -0
  45. package/dist/postgresqlPoolSettings.js.map +1 -0
  46. package/dist/resolvers/AdhocQueryResolver.d.ts +23 -18
  47. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  48. package/dist/resolvers/AdhocQueryResolver.js +77 -167
  49. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  50. package/dist/resolvers/AgentRunStatusPublisher.d.ts +55 -0
  51. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -0
  52. package/dist/resolvers/AgentRunStatusPublisher.js +141 -0
  53. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -0
  54. package/dist/resolvers/ConversationRunEventsResolver.d.ts +90 -0
  55. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -0
  56. package/dist/resolvers/ConversationRunEventsResolver.js +283 -0
  57. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -0
  58. package/dist/resolvers/DuplicateEntryCheckResolver.d.ts +98 -0
  59. package/dist/resolvers/DuplicateEntryCheckResolver.d.ts.map +1 -0
  60. package/dist/resolvers/DuplicateEntryCheckResolver.js +227 -0
  61. package/dist/resolvers/DuplicateEntryCheckResolver.js.map +1 -0
  62. package/dist/resolvers/EntityRecordNameResolver.d.ts +7 -23
  63. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  64. package/dist/resolvers/EntityRecordNameResolver.js +9 -47
  65. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  66. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  67. package/dist/resolvers/FetchEntityVectorsResolver.js +2 -1
  68. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  69. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  70. package/dist/resolvers/GetDataResolver.js +9 -0
  71. package/dist/resolvers/GetDataResolver.js.map +1 -1
  72. package/dist/resolvers/HumanHandoffResolver.d.ts +107 -0
  73. package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -0
  74. package/dist/resolvers/HumanHandoffResolver.js +324 -0
  75. package/dist/resolvers/HumanHandoffResolver.js.map +1 -0
  76. package/dist/resolvers/MeetingResolver.d.ts +69 -0
  77. package/dist/resolvers/MeetingResolver.d.ts.map +1 -0
  78. package/dist/resolvers/MeetingResolver.js +381 -0
  79. package/dist/resolvers/MeetingResolver.js.map +1 -0
  80. package/dist/resolvers/MergeRecordsResolver.d.ts +7 -0
  81. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  82. package/dist/resolvers/MergeRecordsResolver.js +16 -2
  83. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  84. package/dist/resolvers/RealtimeBridgeResolver.d.ts +34 -1
  85. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  86. package/dist/resolvers/RealtimeBridgeResolver.js +202 -7
  87. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  88. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +45 -89
  89. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  90. package/dist/resolvers/RealtimeClientSessionResolver.js +94 -240
  91. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  92. package/dist/resolvers/RunAIAgentResolver.d.ts +0 -16
  93. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  94. package/dist/resolvers/RunAIAgentResolver.js +23 -153
  95. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  96. package/dist/resolvers/RunDecisionResolver.d.ts +149 -0
  97. package/dist/resolvers/RunDecisionResolver.d.ts.map +1 -0
  98. package/dist/resolvers/RunDecisionResolver.js +396 -0
  99. package/dist/resolvers/RunDecisionResolver.js.map +1 -0
  100. package/dist/resolvers/VectorizeEntityResolver.d.ts +1 -1
  101. package/dist/resolvers/VectorizeEntityResolver.d.ts.map +1 -1
  102. package/dist/resolvers/VectorizeEntityResolver.js +14 -10
  103. package/dist/resolvers/VectorizeEntityResolver.js.map +1 -1
  104. package/dist/resolvers/adhocTimeout.d.ts +7 -0
  105. package/dist/resolvers/adhocTimeout.d.ts.map +1 -0
  106. package/dist/resolvers/adhocTimeout.js +12 -0
  107. package/dist/resolvers/adhocTimeout.js.map +1 -0
  108. package/dist/resolvers/meetings/index.d.ts +5 -0
  109. package/dist/resolvers/meetings/index.d.ts.map +1 -0
  110. package/dist/resolvers/meetings/index.js +5 -0
  111. package/dist/resolvers/meetings/index.js.map +1 -0
  112. package/dist/resolvers/meetings/meetingHelpers.d.ts +17 -0
  113. package/dist/resolvers/meetings/meetingHelpers.d.ts.map +1 -0
  114. package/dist/resolvers/meetings/meetingHelpers.js +212 -0
  115. package/dist/resolvers/meetings/meetingHelpers.js.map +1 -0
  116. package/dist/resolvers/meetings/meetingMutations.d.ts +12 -0
  117. package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -0
  118. package/dist/resolvers/meetings/meetingMutations.js +418 -0
  119. package/dist/resolvers/meetings/meetingMutations.js.map +1 -0
  120. package/dist/resolvers/meetings/meetingQueries.d.ts +7 -0
  121. package/dist/resolvers/meetings/meetingQueries.d.ts.map +1 -0
  122. package/dist/resolvers/meetings/meetingQueries.js +113 -0
  123. package/dist/resolvers/meetings/meetingQueries.js.map +1 -0
  124. package/dist/resolvers/meetings/meetingTypes.d.ts +89 -0
  125. package/dist/resolvers/meetings/meetingTypes.d.ts.map +1 -0
  126. package/dist/resolvers/meetings/meetingTypes.js +345 -0
  127. package/dist/resolvers/meetings/meetingTypes.js.map +1 -0
  128. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  129. package/dist/rest/ViewOperationsHandler.js +8 -5
  130. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  131. package/dist/services/TaskGraphActionRunner.d.ts +8 -0
  132. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  133. package/dist/services/TaskGraphActionRunner.js +27 -1
  134. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  135. package/dist/services/WorkQueueHostService.d.ts +36 -0
  136. package/dist/services/WorkQueueHostService.d.ts.map +1 -0
  137. package/dist/services/WorkQueueHostService.js +97 -0
  138. package/dist/services/WorkQueueHostService.js.map +1 -0
  139. package/dist/services/workQueueConfig.d.ts +103 -0
  140. package/dist/services/workQueueConfig.d.ts.map +1 -0
  141. package/dist/services/workQueueConfig.js +48 -0
  142. package/dist/services/workQueueConfig.js.map +1 -0
  143. package/dist/types.d.ts +5 -0
  144. package/dist/types.d.ts.map +1 -1
  145. package/dist/types.js.map +1 -1
  146. package/package.json +95 -92
  147. package/src/__tests__/AdhocQueryResolver.bugs.test.ts +14 -35
  148. package/src/__tests__/AdhocQueryResolver.pagination.test.ts +6 -2
  149. package/src/__tests__/AdhocQueryResolver.scopeGuard.test.ts +3 -3
  150. package/src/__tests__/AgentRunStatusPublisher.test.ts +175 -0
  151. package/src/__tests__/DuplicateEntryCheckResolver.test.ts +300 -0
  152. package/src/__tests__/EntityRecordNameResolver.fieldSecurity.test.ts +27 -72
  153. package/src/__tests__/HumanHandoffResolver.test.ts +233 -0
  154. package/src/__tests__/MeetingResolver.test.ts +374 -0
  155. package/src/__tests__/RealtimeBridgeResolver.test.ts +240 -2
  156. package/src/__tests__/RealtimeClientSessionResolver.test.ts +216 -2
  157. package/src/__tests__/RemoteBrowserActionRecovery.test.ts +7 -1
  158. package/src/__tests__/RemoteBrowserAudioStream.test.ts +7 -1
  159. package/src/__tests__/RemoteBrowserSnapshot.test.ts +7 -1
  160. package/src/__tests__/ResolverBase.adhocReadScope.test.ts +96 -0
  161. package/src/__tests__/ResolverBase.clientOldValues.test.ts +52 -0
  162. package/src/__tests__/ResolverBase.filterEscaping.test.ts +21 -2
  163. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +291 -0
  164. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +182 -12
  165. package/src/__tests__/RunAIAgentResolver.streaming.test.ts +18 -27
  166. package/src/__tests__/RunDecisionResolver.test.ts +699 -0
  167. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +43 -0
  168. package/src/__tests__/VectorizeEntityResolver.test.ts +106 -0
  169. package/src/__tests__/WorkQueueHostService.test.ts +133 -0
  170. package/src/__tests__/adhocTimeoutClamp.test.ts +26 -0
  171. package/src/__tests__/conversationRunEvents.test.ts +399 -0
  172. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +283 -0
  173. package/src/__tests__/postgresqlPoolSettings.test.ts +131 -0
  174. package/src/__tests__/pushStatusFanOut.test.ts +206 -0
  175. package/src/__tests__/translateBracketsToPG.test.ts +20 -0
  176. package/src/auth/__tests__/backward-compatibility.test.ts +1 -0
  177. package/src/config.ts +130 -3
  178. package/src/context.ts +11 -24
  179. package/src/generated/graphql-schemas/__mj.ts +24183 -17182
  180. package/src/generic/FireAndForgetHeartbeat.ts +16 -2
  181. package/src/generic/OrphanedConversationDetailReconciler.ts +267 -0
  182. package/src/generic/PushStatusResolver.ts +97 -1
  183. package/src/generic/ResolverBase.ts +220 -76
  184. package/src/generic/RunViewResolver.ts +33 -2
  185. package/src/index.ts +228 -51
  186. package/src/postgresqlCompat.ts +21 -0
  187. package/src/postgresqlPoolSettings.ts +213 -0
  188. package/src/resolvers/AdhocQueryResolver.ts +88 -188
  189. package/src/resolvers/AgentRunStatusPublisher.ts +175 -0
  190. package/src/resolvers/ConversationRunEventsResolver.ts +308 -0
  191. package/src/resolvers/DuplicateEntryCheckResolver.ts +232 -0
  192. package/src/resolvers/EntityRecordNameResolver.ts +9 -51
  193. package/src/resolvers/FetchEntityVectorsResolver.ts +2 -1
  194. package/src/resolvers/GetDataResolver.ts +10 -0
  195. package/src/resolvers/HumanHandoffResolver.ts +304 -0
  196. package/src/resolvers/MeetingResolver.ts +438 -0
  197. package/src/resolvers/MergeRecordsResolver.ts +14 -2
  198. package/src/resolvers/RealtimeBridgeResolver.ts +227 -13
  199. package/src/resolvers/RealtimeClientSessionResolver.ts +99 -287
  200. package/src/resolvers/RunAIAgentResolver.ts +28 -174
  201. package/src/resolvers/RunDecisionResolver.ts +435 -0
  202. package/src/resolvers/VectorizeEntityResolver.ts +16 -11
  203. package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +93 -0
  204. package/src/resolvers/adhocTimeout.ts +12 -0
  205. package/src/resolvers/meetings/index.ts +4 -0
  206. package/src/resolvers/meetings/meetingHelpers.ts +307 -0
  207. package/src/resolvers/meetings/meetingMutations.ts +535 -0
  208. package/src/resolvers/meetings/meetingQueries.ts +172 -0
  209. package/src/resolvers/meetings/meetingTypes.ts +238 -0
  210. package/src/rest/ViewOperationsHandler.ts +8 -5
  211. package/src/services/TaskGraphActionRunner.ts +29 -1
  212. package/src/services/WorkQueueHostService.ts +129 -0
  213. package/src/services/workQueueConfig.ts +52 -0
  214. package/src/types.ts +5 -0
  215. package/dist/resolvers/adhoc-query-helpers.d.ts +0 -33
  216. package/dist/resolvers/adhoc-query-helpers.d.ts.map +0 -1
  217. package/dist/resolvers/adhoc-query-helpers.js +0 -50
  218. package/dist/resolvers/adhoc-query-helpers.js.map +0 -1
  219. package/src/__tests__/adhoc-query-helpers.test.ts +0 -50
  220. package/src/resolvers/adhoc-query-helpers.ts +0 -67
@@ -0,0 +1,438 @@
1
+ import { Resolver, Mutation, Query, Arg, Ctx } from 'type-graphql';
2
+ import {
3
+ IMetadataProvider,
4
+ LogError,
5
+ } from '@memberjunction/core';
6
+ import type {
7
+ MJMeetingEntity,
8
+ MJMeetingParticipantEntity,
9
+ MJPhoneNumberEntity,
10
+ MJInteractionEntity,
11
+ MJInteractionEventEntity,
12
+ } from '@memberjunction/core-entities';
13
+ import { AppContext } from '../types.js';
14
+ import { ResolverBase } from '../generic/ResolverBase.js';
15
+ import { GetReadWriteProvider } from '../util.js';
16
+ import { SessionManager } from '../agentSessions/SessionManager.js';
17
+
18
+ import {
19
+ MeetingParticipantType,
20
+ MeetingType,
21
+ MeetingResult,
22
+ StartMeetingResult,
23
+ DialInPhoneNumberType,
24
+ CreateMeetingInput,
25
+ UpdateMeetingInput,
26
+ RSVPMeetingInput,
27
+ } from './meetings/meetingTypes.js';
28
+ import {
29
+ LoadMeetingEntity,
30
+ LoadParticipants,
31
+ IsUserHostOrCoHost,
32
+ ResolveDefaultPhoneNumber,
33
+ GenerateUniqueDialInCode,
34
+ EnsureMeetingInteraction,
35
+ RecordInteractionEvent,
36
+ CloseMeetingInteraction,
37
+ SendMeetingInviteNotification,
38
+ NormalizeParticipantRole,
39
+ NormalizeRecordingPolicy,
40
+ MapMeetingToType,
41
+ MapParticipantToType,
42
+ } from './meetings/meetingHelpers.js';
43
+ import {
44
+ ExecuteMyMeetings,
45
+ ExecuteMeeting,
46
+ ExecuteMeetingParticipants,
47
+ ExecuteAvailableDialInPhoneNumbers,
48
+ } from './meetings/meetingQueries.js';
49
+ import {
50
+ ExecuteCreateMeeting,
51
+ ExecuteUpdateMeeting,
52
+ ExecuteStartMeeting,
53
+ ExecuteEndMeeting,
54
+ ExecuteCancelMeeting,
55
+ ExecuteRSVPMeeting,
56
+ type MeetingHelpers,
57
+ } from './meetings/meetingMutations.js';
58
+
59
+ // Re-export all meeting types and helpers for external consumers
60
+ export * from './meetings/index.js';
61
+
62
+ @Resolver()
63
+ export class MeetingResolver extends ResolverBase {
64
+ protected readonly sessionManager = new SessionManager();
65
+
66
+ // ── Queries ──────────────────────────────────────────────────────────────────
67
+
68
+ /**
69
+ * Lists meetings accessible to the caller (meetings where user is host or participant).
70
+ */
71
+ @Query(() => [MeetingType])
72
+ async MyMeetings(
73
+ @Arg('status', () => String, { nullable: true }) status?: string,
74
+ @Ctx() context: AppContext = {} as AppContext,
75
+ ): Promise<MeetingType[]> {
76
+ try {
77
+ const user = this.GetUserFromPayload(context.userPayload);
78
+ if (!user) {
79
+ throw new Error('User is not authenticated.');
80
+ }
81
+ const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
82
+ if (!provider) {
83
+ throw new Error('Database provider is not available.');
84
+ }
85
+ return await ExecuteMyMeetings(provider, user, status);
86
+ } catch (error) {
87
+ LogError(`[MeetingResolver] MyMeetings failed: ${error instanceof Error ? error.message : String(error)}`);
88
+ return [];
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Gets a single meeting by ID.
94
+ */
95
+ @Query(() => MeetingType, { nullable: true })
96
+ async Meeting(
97
+ @Arg('id', () => String) id: string,
98
+ @Ctx() context: AppContext = {} as AppContext,
99
+ ): Promise<MeetingType | null> {
100
+ try {
101
+ const user = this.GetUserFromPayload(context.userPayload);
102
+ if (!user) {
103
+ throw new Error('User is not authenticated.');
104
+ }
105
+ const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
106
+ if (!provider) {
107
+ throw new Error('Database provider is not available.');
108
+ }
109
+ return await ExecuteMeeting(provider, user, id);
110
+ } catch (error) {
111
+ LogError(`[MeetingResolver] Meeting query failed: ${error instanceof Error ? error.message : String(error)}`);
112
+ return null;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Gets participants for a given meeting.
118
+ */
119
+ @Query(() => [MeetingParticipantType])
120
+ async MeetingParticipants(
121
+ @Arg('meetingID', () => String) meetingID: string,
122
+ @Ctx() context: AppContext = {} as AppContext,
123
+ ): Promise<MeetingParticipantType[]> {
124
+ try {
125
+ const user = this.GetUserFromPayload(context.userPayload);
126
+ if (!user) {
127
+ throw new Error('User is not authenticated.');
128
+ }
129
+ const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
130
+ if (!provider) {
131
+ throw new Error('Database provider is not available.');
132
+ }
133
+ return await ExecuteMeetingParticipants(provider, user, meetingID);
134
+ } catch (error) {
135
+ LogError(`[MeetingResolver] MeetingParticipants failed: ${error instanceof Error ? error.message : String(error)}`);
136
+ return [];
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Lists available active phone numbers suitable for dial-in.
142
+ */
143
+ @Query(() => [DialInPhoneNumberType])
144
+ async AvailableDialInPhoneNumbers(
145
+ @Ctx() context: AppContext = {} as AppContext,
146
+ ): Promise<DialInPhoneNumberType[]> {
147
+ try {
148
+ const user = this.GetUserFromPayload(context.userPayload);
149
+ if (!user) {
150
+ throw new Error('User is not authenticated.');
151
+ }
152
+ const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
153
+ if (!provider) {
154
+ throw new Error('Database provider is not available.');
155
+ }
156
+ return await ExecuteAvailableDialInPhoneNumbers(provider, user);
157
+ } catch (error) {
158
+ LogError(`[MeetingResolver] AvailableDialInPhoneNumbers failed: ${error instanceof Error ? error.message : String(error)}`);
159
+ return [];
160
+ }
161
+ }
162
+
163
+ // ── Mutations ────────────────────────────────────────────────────────────────
164
+
165
+ /**
166
+ * Creates and schedules a new meeting.
167
+ */
168
+ @Mutation(() => MeetingResult)
169
+ async CreateMeeting(
170
+ @Arg('input', () => CreateMeetingInput) input: CreateMeetingInput,
171
+ @Ctx() context: AppContext = {} as AppContext,
172
+ ): Promise<MeetingResult> {
173
+ try {
174
+ const user = this.GetUserFromPayload(context.userPayload);
175
+ if (!user) {
176
+ return { Success: false, ErrorMessage: 'User is not authenticated.' };
177
+ }
178
+ const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
179
+ if (!provider) {
180
+ return { Success: false, ErrorMessage: 'Database provider is not available.' };
181
+ }
182
+ return await ExecuteCreateMeeting(provider, user, input, this.getHelperOverrides());
183
+ } catch (error) {
184
+ const msg = error instanceof Error ? error.message : String(error);
185
+ LogError(`[MeetingResolver] CreateMeeting exception: ${msg}`);
186
+ return { Success: false, ErrorMessage: msg };
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Updates meeting details and participant list. Host or CoHost only.
192
+ */
193
+ @Mutation(() => MeetingResult)
194
+ async UpdateMeeting(
195
+ @Arg('input', () => UpdateMeetingInput) input: UpdateMeetingInput,
196
+ @Ctx() context: AppContext = {} as AppContext,
197
+ ): Promise<MeetingResult> {
198
+ try {
199
+ const user = this.GetUserFromPayload(context.userPayload);
200
+ if (!user) {
201
+ return { Success: false, ErrorMessage: 'User is not authenticated.' };
202
+ }
203
+ const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
204
+ if (!provider) {
205
+ return { Success: false, ErrorMessage: 'Database provider is not available.' };
206
+ }
207
+ return await ExecuteUpdateMeeting(provider, user, input, this.getHelperOverrides());
208
+ } catch (error) {
209
+ const msg = error instanceof Error ? error.message : String(error);
210
+ LogError(`[MeetingResolver] UpdateMeeting exception: ${msg}`);
211
+ return { Success: false, ErrorMessage: msg };
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Starts a meeting: transitions status to 'Live', creates or connects to the
217
+ * meeting Interaction record, launches configured AI agents, initiates recording
218
+ * if configured as Automatic, and returns a LiveKit client token for the caller.
219
+ */
220
+ @Mutation(() => StartMeetingResult)
221
+ async StartMeeting(
222
+ @Arg('meetingID', () => String) meetingID: string,
223
+ @Ctx() context: AppContext = {} as AppContext,
224
+ ): Promise<StartMeetingResult> {
225
+ try {
226
+ const user = this.GetUserFromPayload(context.userPayload);
227
+ if (!user) {
228
+ return { Success: false, ErrorMessage: 'User is not authenticated.' };
229
+ }
230
+ const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
231
+ if (!provider) {
232
+ return { Success: false, ErrorMessage: 'Database provider is not available.' };
233
+ }
234
+ return await ExecuteStartMeeting(provider, user, meetingID, this.sessionManager, this.getHelperOverrides());
235
+ } catch (error) {
236
+ const msg = error instanceof Error ? error.message : String(error);
237
+ LogError(`[MeetingResolver] StartMeeting exception: ${msg}`);
238
+ return { Success: false, ErrorMessage: msg };
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Ends a meeting: transitions status to 'Ended', stops all agents in the room,
244
+ * closes the Interaction record, and records lifecycle events.
245
+ */
246
+ @Mutation(() => MeetingResult)
247
+ async EndMeeting(
248
+ @Arg('meetingID', () => String) meetingID: string,
249
+ @Ctx() context: AppContext = {} as AppContext,
250
+ ): Promise<MeetingResult> {
251
+ try {
252
+ const user = this.GetUserFromPayload(context.userPayload);
253
+ if (!user) {
254
+ return { Success: false, ErrorMessage: 'User is not authenticated.' };
255
+ }
256
+ const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
257
+ if (!provider) {
258
+ return { Success: false, ErrorMessage: 'Database provider is not available.' };
259
+ }
260
+ return await ExecuteEndMeeting(provider, user, meetingID, this.getHelperOverrides());
261
+ } catch (error) {
262
+ const msg = error instanceof Error ? error.message : String(error);
263
+ LogError(`[MeetingResolver] EndMeeting exception: ${msg}`);
264
+ return { Success: false, ErrorMessage: msg };
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Cancels a scheduled meeting.
270
+ */
271
+ @Mutation(() => MeetingResult)
272
+ async CancelMeeting(
273
+ @Arg('meetingID', () => String) meetingID: string,
274
+ @Ctx() context: AppContext = {} as AppContext,
275
+ ): Promise<MeetingResult> {
276
+ try {
277
+ const user = this.GetUserFromPayload(context.userPayload);
278
+ if (!user) {
279
+ return { Success: false, ErrorMessage: 'User is not authenticated.' };
280
+ }
281
+ const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
282
+ if (!provider) {
283
+ return { Success: false, ErrorMessage: 'Database provider is not available.' };
284
+ }
285
+ return await ExecuteCancelMeeting(provider, user, meetingID, this.getHelperOverrides());
286
+ } catch (error) {
287
+ const msg = error instanceof Error ? error.message : String(error);
288
+ LogError(`[MeetingResolver] CancelMeeting exception: ${msg}`);
289
+ return { Success: false, ErrorMessage: msg };
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Updates RSVP invite status for the authenticated user.
295
+ */
296
+ @Mutation(() => MeetingResult)
297
+ async RSVPMeeting(
298
+ @Arg('input', () => RSVPMeetingInput) input: RSVPMeetingInput,
299
+ @Ctx() context: AppContext = {} as AppContext,
300
+ ): Promise<MeetingResult> {
301
+ try {
302
+ const user = this.GetUserFromPayload(context.userPayload);
303
+ if (!user) {
304
+ return { Success: false, ErrorMessage: 'User is not authenticated.' };
305
+ }
306
+ const provider: IMetadataProvider | null = GetReadWriteProvider(context.providers);
307
+ if (!provider) {
308
+ return { Success: false, ErrorMessage: 'Database provider is not available.' };
309
+ }
310
+ return await ExecuteRSVPMeeting(provider, user, input, this.getHelperOverrides());
311
+ } catch (error) {
312
+ const msg = error instanceof Error ? error.message : String(error);
313
+ LogError(`[MeetingResolver] RSVPMeeting exception: ${msg}`);
314
+ return { Success: false, ErrorMessage: msg };
315
+ }
316
+ }
317
+
318
+ // ── Helper Overrides & Delegations ───────────────────────────────────────────
319
+
320
+ protected getHelperOverrides(): Partial<MeetingHelpers> {
321
+ return {
322
+ LoadMeetingEntity: (id, user, provider) => this.loadMeetingEntity(id, user, provider),
323
+ LoadParticipants: (meetingID, user, provider) => this.loadParticipants(meetingID, user, provider),
324
+ IsUserHostOrCoHost: (meeting, user, provider) => this.isUserHostOrCoHost(meeting, user, provider),
325
+ ResolveDefaultPhoneNumber: (user, provider) => this.resolveDefaultPhoneNumber(user, provider),
326
+ GenerateUniqueDialInCode: (phoneNumberID, user, provider) => this.generateUniqueDialInCode(phoneNumberID, user, provider),
327
+ EnsureMeetingInteraction: (meeting, user, provider) => this.ensureMeetingInteraction(meeting, user, provider),
328
+ RecordInteractionEvent: (interactionID, eventType, user, provider, details) =>
329
+ this.recordInteractionEvent(interactionID, eventType, user, provider, details),
330
+ CloseMeetingInteraction: (roomName, user, provider) => this.closeMeetingInteraction(roomName, user, provider),
331
+ SendMeetingInviteNotification: (meeting, targetUserID, contextUser, provider) =>
332
+ this.sendMeetingInviteNotification(meeting, targetUserID, contextUser, provider),
333
+ NormalizeParticipantRole: (roleInput, defaultRole) => this.normalizeParticipantRole(roleInput, defaultRole),
334
+ NormalizeRecordingPolicy: (policyInput) => this.normalizeRecordingPolicy(policyInput),
335
+ MapMeetingToType: (m, participants) => this.mapMeetingToType(m, participants),
336
+ MapParticipantToType: (p, userName, userEmail) => this.mapParticipantToType(p, userName, userEmail),
337
+ };
338
+ }
339
+
340
+ protected async loadMeetingEntity(
341
+ id: string,
342
+ user: import('@memberjunction/core').UserInfo,
343
+ provider: IMetadataProvider,
344
+ ): Promise<MJMeetingEntity | null> {
345
+ return LoadMeetingEntity(id, user, provider);
346
+ }
347
+
348
+ protected async loadParticipants(
349
+ meetingID: string,
350
+ user: import('@memberjunction/core').UserInfo,
351
+ provider: IMetadataProvider,
352
+ ): Promise<MeetingParticipantType[]> {
353
+ return LoadParticipants(meetingID, user, provider);
354
+ }
355
+
356
+ protected async isUserHostOrCoHost(
357
+ meeting: MJMeetingEntity,
358
+ user: import('@memberjunction/core').UserInfo,
359
+ provider: IMetadataProvider,
360
+ ): Promise<boolean> {
361
+ return IsUserHostOrCoHost(meeting, user, provider);
362
+ }
363
+
364
+ protected async resolveDefaultPhoneNumber(
365
+ user: import('@memberjunction/core').UserInfo,
366
+ provider: IMetadataProvider,
367
+ ): Promise<MJPhoneNumberEntity | null> {
368
+ return ResolveDefaultPhoneNumber(user, provider);
369
+ }
370
+
371
+ protected async generateUniqueDialInCode(
372
+ phoneNumberID: string,
373
+ user: import('@memberjunction/core').UserInfo,
374
+ provider: IMetadataProvider,
375
+ ): Promise<string | undefined> {
376
+ return GenerateUniqueDialInCode(phoneNumberID, user, provider);
377
+ }
378
+
379
+ protected async ensureMeetingInteraction(
380
+ meeting: MJMeetingEntity,
381
+ user: import('@memberjunction/core').UserInfo,
382
+ provider: IMetadataProvider,
383
+ ): Promise<MJInteractionEntity | null> {
384
+ return EnsureMeetingInteraction(meeting, user, provider);
385
+ }
386
+
387
+ protected async recordInteractionEvent(
388
+ interactionID: string,
389
+ eventType: MJInteractionEventEntity['EventType'],
390
+ user: import('@memberjunction/core').UserInfo,
391
+ provider: IMetadataProvider,
392
+ details?: Record<string, unknown>,
393
+ ): Promise<void> {
394
+ return RecordInteractionEvent(interactionID, eventType, user, provider, details);
395
+ }
396
+
397
+ protected async closeMeetingInteraction(
398
+ roomName: string,
399
+ user: import('@memberjunction/core').UserInfo,
400
+ provider: IMetadataProvider,
401
+ ): Promise<void> {
402
+ return CloseMeetingInteraction(roomName, user, provider);
403
+ }
404
+
405
+ protected async sendMeetingInviteNotification(
406
+ meeting: MJMeetingEntity,
407
+ targetUserID: string,
408
+ contextUser: import('@memberjunction/core').UserInfo,
409
+ provider: IMetadataProvider,
410
+ ): Promise<void> {
411
+ return SendMeetingInviteNotification(meeting, targetUserID, contextUser, provider);
412
+ }
413
+
414
+ protected normalizeParticipantRole(
415
+ roleInput?: string,
416
+ defaultRole: MJMeetingParticipantEntity['Role'] = 'Attendee',
417
+ ): MJMeetingParticipantEntity['Role'] {
418
+ return NormalizeParticipantRole(roleInput, defaultRole);
419
+ }
420
+
421
+ protected normalizeRecordingPolicy(
422
+ policyInput?: string,
423
+ ): MJMeetingEntity['RecordingPolicy'] {
424
+ return NormalizeRecordingPolicy(policyInput);
425
+ }
426
+
427
+ protected mapMeetingToType(m: MJMeetingEntity, participants?: MeetingParticipantType[]): MeetingType {
428
+ return MapMeetingToType(m, participants);
429
+ }
430
+
431
+ protected mapParticipantToType(
432
+ p: MJMeetingParticipantEntity,
433
+ userNameFallback?: string | null,
434
+ userEmailFallback?: string | null,
435
+ ): MeetingParticipantType {
436
+ return MapParticipantToType(p, userNameFallback, userEmailFallback);
437
+ }
438
+ }
@@ -50,7 +50,17 @@ export class RecordDependencyResult {
50
50
  FieldName: string; // required
51
51
 
52
52
  @Field(() => CompositeKeyOutputType)
53
+ PrimaryKey: CompositeKey;
54
+
55
+ /** The same key under its pre-6.2 name, so older clients keep working. */
56
+ @Field(() => CompositeKeyOutputType, { deprecationReason: 'Use PrimaryKey. CompositeKey will be removed in a future release.' })
53
57
  CompositeKey: CompositeKey;
58
+
59
+ @Field(() => Boolean, { nullable: true })
60
+ IsSoftLink?: boolean;
61
+
62
+ @Field(() => String, { nullable: true })
63
+ EntityIDFieldName?: string;
54
64
  }
55
65
 
56
66
  @Resolver(RecordDependencyResult)
@@ -67,12 +77,14 @@ export class RecordDependencyResolver {
67
77
  const ck = new CompositeKey(ckInput.KeyValuePairs);
68
78
  const result = await md.GetRecordDependencies(entityName, ck);
69
79
 
70
- // Map PrimaryKey to CompositeKey for GraphQL response
71
80
  return result.map(dep => ({
72
81
  EntityName: dep.EntityName,
73
82
  RelatedEntityName: dep.RelatedEntityName,
74
83
  FieldName: dep.FieldName,
75
- CompositeKey: dep.PrimaryKey // Map PrimaryKey to CompositeKey
84
+ PrimaryKey: dep.PrimaryKey,
85
+ CompositeKey: dep.PrimaryKey,
86
+ IsSoftLink: dep.IsSoftLink,
87
+ EntityIDFieldName: dep.EntityIDFieldName,
76
88
  }));
77
89
  } catch (e) {
78
90
  LogError(e);