@memberjunction/communication-ms-graph 5.48.0 → 5.50.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.
@@ -1,4 +1,4 @@
1
- import { BaseCommunicationProvider, CreateDraftParams, CreateDraftResult, ForwardMessageParams, ForwardMessageResult, GetMessagesParams, GetMessagesResult, GetSingleMessageParams, GetSingleMessageResult, DeleteMessageParams, DeleteMessageResult, MoveMessageParams, MoveMessageResult, ListFoldersParams, ListFoldersResult, MarkAsReadParams, MarkAsReadResult, ArchiveMessageParams, ArchiveMessageResult, SearchMessagesParams, SearchMessagesResult, ListAttachmentsParams, ListAttachmentsResult, DownloadAttachmentParams, DownloadAttachmentResult, ProviderOperation, MessageResult, ProcessedMessage, ProviderCredentialsBase, ReplyToMessageParams, ReplyToMessageResult } from "@memberjunction/communication-types";
1
+ import { BaseCommunicationProvider, CreateDraftParams, CreateDraftResult, ForwardMessageParams, ForwardMessageResult, GetMessagesParams, GetMessagesResult, GetSingleMessageParams, GetSingleMessageResult, DeleteMessageParams, DeleteMessageResult, MoveMessageParams, MoveMessageResult, ListFoldersParams, ListFoldersResult, MarkAsReadParams, MarkAsReadResult, ArchiveMessageParams, ArchiveMessageResult, SearchMessagesParams, SearchMessagesResult, ListAttachmentsParams, ListAttachmentsResult, DownloadAttachmentParams, DownloadAttachmentResult, ProviderOperation, MessageResult, ProcessedMessage, ProviderCredentialsBase, ReplyToMessageParams, ReplyToMessageResult, CreateSubscriptionParams, RenewSubscriptionParams, DeleteSubscriptionParams, SubscriptionResult, SubscriptionCapabilities, WebhookNotificationInput, ParseNotificationResult, BaseMessageResult } from "@memberjunction/communication-types";
2
2
  import { Client } from '@microsoft/microsoft-graph-client';
3
3
  import { Message } from "@microsoft/microsoft-graph-types";
4
4
  /**
@@ -99,6 +99,11 @@ export declare class MSGraphProvider extends BaseCommunicationProvider {
99
99
  * Gets the API URI for Graph API calls.
100
100
  */
101
101
  private getApiUri;
102
+ /**
103
+ * Flattens a Graph recipient collection (toRecipients/ccRecipients/bccRecipients)
104
+ * into bare email addresses, dropping entries with no resolvable address.
105
+ */
106
+ private extractRecipientAddresses;
102
107
  /**
103
108
  * Sends a single email message via MS Graph.
104
109
  *
@@ -208,6 +213,77 @@ export declare class MSGraphProvider extends BaseCommunicationProvider {
208
213
  * MS Graph supports all mailbox operations.
209
214
  */
210
215
  getSupportedOperations(): ProviderOperation[];
216
+ /**
217
+ * Builds the base URL for Graph subscription resources. Subscriptions live at the
218
+ * Graph service root (`/v1.0/subscriptions`), NOT under the users-rooted
219
+ * {@link getApiUri}. Built from `AZURE_GRAPH_ENDPOINT` so sovereign-cloud overrides
220
+ * are honored.
221
+ */
222
+ private getSubscriptionsUri;
223
+ /**
224
+ * Resolves the Graph `resource` folder segment for a subscription. Returns the
225
+ * segment to embed in `mailFolders('<segment>')`, or `null` when a custom folder name
226
+ * could not be resolved to an ID.
227
+ *
228
+ * Hot path first: an explicit `folderId` is used verbatim, and a well-known folder
229
+ * name is passed through - both with zero extra Graph calls. Only a custom display
230
+ * name incurs a resolution lookup.
231
+ */
232
+ private resolveSubscriptionFolderSegment;
233
+ /**
234
+ * Creates a Microsoft Graph change-notification subscription for messages in a
235
+ * mailbox folder. Graph validates the notification endpoint synchronously during
236
+ * this call (it must echo the validation token), so an unreachable/incorrect endpoint
237
+ * surfaces here as a Graph error.
238
+ *
239
+ * @requires MS Graph Scope: Mail.Read (Application)
240
+ * @param params - What to watch, where to notify, and the clientState secret
241
+ * @param credentials - Optional credentials override for this request
242
+ * @returns Promise<SubscriptionResult> - Subscription ID and expiration on success
243
+ */
244
+ CreateSubscription(params: CreateSubscriptionParams, credentials?: MSGraphCredentials): Promise<SubscriptionResult>;
245
+ /**
246
+ * Renews an existing Graph subscription before it expires. The caller MUST pass
247
+ * credentials for the same app registration that created the subscription - Graph
248
+ * subscriptions are visible only to their creator, so a credential mismatch surfaces
249
+ * as a 404.
250
+ *
251
+ * @requires MS Graph Scope: Mail.Read (Application)
252
+ * @param params - The subscription ID and requested new expiration
253
+ * @param credentials - Optional credentials override for this request
254
+ * @returns Promise<SubscriptionResult> - The renewed expiration on success
255
+ */
256
+ RenewSubscription(params: RenewSubscriptionParams, credentials?: MSGraphCredentials): Promise<SubscriptionResult>;
257
+ /**
258
+ * Deletes an existing Graph subscription. Idempotent from the consumer's perspective:
259
+ * a 404 (already gone) is treated as success. The caller MUST pass credentials for
260
+ * the same app registration that created the subscription.
261
+ *
262
+ * @requires MS Graph Scope: Mail.Read (Application)
263
+ * @param params - The subscription ID to delete
264
+ * @param credentials - Optional credentials override for this request
265
+ * @returns Promise<BaseMessageResult> - Result of the delete operation
266
+ */
267
+ DeleteSubscription(params: DeleteSubscriptionParams, credentials?: MSGraphCredentials): Promise<BaseMessageResult>;
268
+ /**
269
+ * Parses and validates an inbound Graph change notification. Pure: no Graph client,
270
+ * no network. Safe on hostile/garbage input - never throws; returns `Success: false`
271
+ * with a 400 suggested status on malformed payloads.
272
+ *
273
+ * Graph has no cryptographic signature scheme, so `SignatureValid` is left undefined;
274
+ * the consumer authenticates each notification by comparing its `ClientState` against
275
+ * the secret stored alongside the subscription.
276
+ *
277
+ * @param input - Transport-neutral capture of the inbound webhook request
278
+ * @returns Promise<ParseNotificationResult> - Handshake or normalized notifications
279
+ */
280
+ ParseNotification(input: WebhookNotificationInput, _credentials?: MSGraphCredentials): Promise<ParseNotificationResult>;
281
+ /**
282
+ * Returns MS Graph's subscription capabilities. Graph mail subscriptions last at most
283
+ * 4230 minutes (~3 days), support all three change types, and require synchronous
284
+ * endpoint validation at create time.
285
+ */
286
+ GetSubscriptionCapabilities(): SubscriptionCapabilities;
211
287
  /**
212
288
  * Gets a single message by ID from MS Graph.
213
289
  *
@@ -285,5 +361,42 @@ export declare class MSGraphProvider extends BaseCommunicationProvider {
285
361
  * Maps folder display name to system folder type.
286
362
  */
287
363
  private mapSystemFolderType;
364
+ /**
365
+ * Resolves a folder name to its Microsoft Graph well-known name when it is one,
366
+ * matching case-insensitively against both friendly aliases and the canonical Graph
367
+ * names. Graph accepts a well-known name directly in a resource path (no ID-resolution
368
+ * call needed). Returns undefined for a custom display name, which then requires a
369
+ * lookup. Shared by findSystemFolder and the CreateSubscription resource builder.
370
+ */
371
+ private resolveWellKnownFolder;
372
+ /**
373
+ * Extracts an HTTP status code from a thrown Microsoft Graph client error. The
374
+ * middleware client throws a GraphError-shaped object carrying a numeric statusCode;
375
+ * this narrows an untyped catch value to that code (or undefined when absent).
376
+ */
377
+ private getGraphStatusCode;
378
+ /**
379
+ * Parses the mailbox identifier (user ID / email) out of a Graph change-notification
380
+ * resource string. Tolerant of both Users/{id}/Messages/{msgId} and
381
+ * users/{id}/mailFolders('...')/messages/{msgId} shapes, case-insensitively. Returns
382
+ * undefined when no pattern matches - the identifier is a routing convenience only.
383
+ */
384
+ private parseIdentifierFromResource;
385
+ /**
386
+ * Maps a raw Graph changeType value onto the normalized SubscriptionChangeType union,
387
+ * returning undefined for anything unrecognized.
388
+ */
389
+ private mapChangeType;
390
+ /**
391
+ * Maps a raw Graph lifecycleEvent value onto the normalized lifecycle-event union,
392
+ * returning undefined for anything unrecognized.
393
+ */
394
+ private mapLifecycleEvent;
395
+ /**
396
+ * Clamps a requested subscription expiration to Graph's maximum lifetime, returning an
397
+ * ISO-8601 UTC string. When no expiration is requested (or it exceeds the max), the
398
+ * maximum-allowed expiration from now is used.
399
+ */
400
+ private clampExpiration;
288
401
  }
289
402
  //# sourceMappingURL=MSGraphProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MSGraphProvider.d.ts","sourceRoot":"","sources":["../src/MSGraphProvider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MSGraphProvider.d.ts","sourceRoot":"","sources":["../src/MSGraphProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,wBAAwB,EAGxB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EAGpB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,EAClB,wBAAwB,EAExB,wBAAwB,EACxB,uBAAuB,EAEvB,iBAAiB,EACpB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAG3D,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAO3D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IAC/D;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAiCD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBACa,eAAgB,SAAQ,yBAAyB;IAE1D,OAAO,CAAC,aAAa,CAAmB;IAExC;;;;OAIG;IACH,OAAO,CAAC,WAAW,CAGhB;;IAUH;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAsB1B;;;OAGG;IACH,OAAO,CAAC,cAAc;IAkCtB;;OAEG;IACH,OAAO,CAAC,SAAS;IAIjB;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAIjC;;;;OAIG;IACU,iBAAiB,CAC1B,OAAO,EAAE,gBAAgB,EACzB,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,aAAa,CAAC;IA4FzB;;;;OAIG;IACU,cAAc,CACvB,MAAM,EAAE,oBAAoB,EAC5B,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,oBAAoB,CAAC;IA8ChC;;;;OAIG;IACU,WAAW,CACpB,MAAM,EAAE,iBAAiB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAClD,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAuFtC;;;;OAIG;IACU,cAAc,CACvB,MAAM,EAAE,oBAAoB,EAC5B,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,oBAAoB,CAAC;IAiDhC;;;;;;OAMG;cACa,mBAAmB,CAC/B,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,MAAM,GAAG,SAAS,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAWlC;;;;;;OAMG;cACa,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBrH;;;;;;OAMG;cACa,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAuBvI;;;;;;OAMG;cACa,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAK9G;;;;;;OAMG;cACa,oBAAoB,CAChC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,EACjD,SAAS,EAAE,MAAM,GAAG,SAAS,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAgBlC;;;;;OAKG;cACa,UAAU,CAAC,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAIxK;;;;;;OAMG;cACa,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBhH;;;;;OAKG;cACa,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAItF;;;;OAIG;IACU,WAAW,CACpB,MAAM,EAAE,iBAAiB,EACzB,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,iBAAiB,CAAC;IAgE7B;;;OAGG;IACa,sBAAsB,IAAI,iBAAiB,EAAE;IA8B7D;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;;;;;;;OAQG;YACW,gCAAgC;IAiB9C;;;;;;;;;;OAUG;IACmB,kBAAkB,CACpC,MAAM,EAAE,wBAAwB,EAChC,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,kBAAkB,CAAC;IA2D9B;;;;;;;;;;OAUG;IACmB,iBAAiB,CACnC,MAAM,EAAE,uBAAuB,EAC/B,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,kBAAkB,CAAC;IA6B9B;;;;;;;;;OASG;IACmB,kBAAkB,CACpC,MAAM,EAAE,wBAAwB,EAChC,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,iBAAiB,CAAC;IAmB7B;;;;;;;;;;;OAWG;IACmB,iBAAiB,CACnC,KAAK,EAAE,wBAAwB,EAC/B,YAAY,CAAC,EAAE,kBAAkB,GAClC,OAAO,CAAC,uBAAuB,CAAC;IAiEnC;;;;OAIG;IACa,2BAA2B,IAAI,wBAAwB;IAavE;;;;OAIG;IACmB,gBAAgB,CAClC,MAAM,EAAE,sBAAsB,EAC9B,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,sBAAsB,CAAC;IAgDlC;;;;;OAKG;IACmB,aAAa,CAC/B,MAAM,EAAE,mBAAmB,EAC3B,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,mBAAmB,CAAC;IAoC/B;;;;OAIG;IACmB,WAAW,CAC7B,MAAM,EAAE,iBAAiB,EACzB,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,iBAAiB,CAAC;IA2B7B;;;;OAIG;IACmB,WAAW,CAC7B,MAAM,EAAE,iBAAiB,EACzB,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,iBAAiB,CAAC;IA+C7B;;;;OAIG;IACmB,UAAU,CAC5B,MAAM,EAAE,gBAAgB,EACxB,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,gBAAgB,CAAC;IAyB5B;;;;OAIG;IACmB,cAAc,CAChC,MAAM,EAAE,oBAAoB,EAC5B,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,oBAAoB,CAAC;IAqChC;;;;OAIG;IACmB,cAAc,CAChC,MAAM,EAAE,oBAAoB,EAC5B,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,oBAAoB,CAAC;IA6EhC;;;;OAIG;IACmB,eAAe,CACjC,MAAM,EAAE,qBAAqB,EAC7B,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,qBAAqB,CAAC;IAwCjC;;;;OAIG;IACmB,kBAAkB,CACpC,MAAM,EAAE,wBAAwB,EAChC,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,wBAAwB,CAAC;IAyCpC;;;;;OAKG;YACW,gBAAgB;IA2B9B;;;;;OAKG;YACW,gBAAgB;IAe9B;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAY3B;;;;;;OAMG;IACH,OAAO,CAAC,sBAAsB;IAe9B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;;;;OAKG;IACH,OAAO,CAAC,2BAA2B;IAQnC;;;OAGG;IACH,OAAO,CAAC,aAAa;IAOrB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;;;OAIG;IACH,OAAO,CAAC,eAAe;CAK1B"}
@@ -16,6 +16,11 @@ import { LogError, LogStatus } from "@memberjunction/core";
16
16
  import { compile } from 'html-to-text';
17
17
  import * as Auth from "./auth.js";
18
18
  import * as Config from "./config.js";
19
+ /**
20
+ * The maximum lifetime, in minutes, that Microsoft Graph allows for a mail-resource
21
+ * change-notification subscription (~3 days). Requested expirations are clamped to this.
22
+ */
23
+ const MSGRAPH_MAX_SUBSCRIPTION_MINUTES = 4230;
19
24
  /**
20
25
  * Implementation of the MS Graph provider for sending and receiving messages.
21
26
  *
@@ -106,6 +111,13 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
106
111
  getApiUri() {
107
112
  return Auth.ApiConfig.uri;
108
113
  }
114
+ /**
115
+ * Flattens a Graph recipient collection (toRecipients/ccRecipients/bccRecipients)
116
+ * into bare email addresses, dropping entries with no resolvable address.
117
+ */
118
+ extractRecipientAddresses(recipients) {
119
+ return recipients?.map((recipient) => recipient.emailAddress?.address || '').filter((address) => address.length > 0) || [];
120
+ }
109
121
  /**
110
122
  * Sends a single email message via MS Graph.
111
123
  *
@@ -166,6 +178,18 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
166
178
  }
167
179
  // Use email address directly in API path instead of looking up user ID
168
180
  const sendMessagePath = `${this.getApiUri()}/${encodeURIComponent(senderEmail)}/sendMail`;
181
+ // DRY RUN: full pipeline ran (credential resolution/validation, sender selection,
182
+ // complete Graph sendMail payload + headers + API path construction above) — stop at
183
+ // the transport boundary, never calling Microsoft Graph.
184
+ if (message.DryRun) {
185
+ LogStatus(`[DryRun] Microsoft Graph: sendMail payload constructed for ${message.To} — external send skipped`);
186
+ return {
187
+ Message: message,
188
+ Success: true,
189
+ Error: '',
190
+ DryRun: true
191
+ };
192
+ }
169
193
  await client.api(sendMessagePath).post(sendMail);
170
194
  return {
171
195
  Message: message,
@@ -280,6 +304,8 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
280
304
  return {
281
305
  From: msgTyped.from?.emailAddress?.address || '',
282
306
  To: primaryToRecipient,
307
+ ToRecipients: this.extractRecipientAddresses(msgTyped.toRecipients),
308
+ CCRecipients: this.extractRecipientAddresses(msgTyped.ccRecipients),
283
309
  ReplyTo: replyTo,
284
310
  Subject: msgTyped.subject || '',
285
311
  Body: contextData?.ReturnAsPlainText || contextData?.ReturnAsPlainTex ? this.HTMLConverter(msgTyped.body?.content || '') : msgTyped.body?.content || '',
@@ -568,9 +594,274 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
568
594
  'ArchiveMessage',
569
595
  'SearchMessages',
570
596
  'ListAttachments',
571
- 'DownloadAttachment'
597
+ 'DownloadAttachment',
598
+ 'CreateSubscription',
599
+ 'RenewSubscription',
600
+ 'DeleteSubscription',
601
+ 'ParseNotification'
572
602
  ];
573
603
  }
604
+ // ========================================================================
605
+ // PUSH-NOTIFICATION SUBSCRIPTIONS
606
+ // Graph change-notification subscriptions. CRUD rides the existing authenticated
607
+ // client; ParseNotification is pure (no client, no network). The provider is
608
+ // stateless - the consumer persists subscription IDs/expirations/secrets.
609
+ // ========================================================================
610
+ /**
611
+ * Builds the base URL for Graph subscription resources. Subscriptions live at the
612
+ * Graph service root (`/v1.0/subscriptions`), NOT under the users-rooted
613
+ * {@link getApiUri}. Built from `AZURE_GRAPH_ENDPOINT` so sovereign-cloud overrides
614
+ * are honored.
615
+ */
616
+ getSubscriptionsUri() {
617
+ return `${Config.AZURE_GRAPH_ENDPOINT}/v1.0/subscriptions`;
618
+ }
619
+ /**
620
+ * Resolves the Graph `resource` folder segment for a subscription. Returns the
621
+ * segment to embed in `mailFolders('<segment>')`, or `null` when a custom folder name
622
+ * could not be resolved to an ID.
623
+ *
624
+ * Hot path first: an explicit `folderId` is used verbatim, and a well-known folder
625
+ * name is passed through - both with zero extra Graph calls. Only a custom display
626
+ * name incurs a resolution lookup.
627
+ */
628
+ async resolveSubscriptionFolderSegment(client, mailboxId, context) {
629
+ if (context?.folderId) {
630
+ return context.folderId;
631
+ }
632
+ const folderName = context?.folderName || 'inbox';
633
+ const wellKnown = this.resolveWellKnownFolder(folderName);
634
+ if (wellKnown) {
635
+ return wellKnown;
636
+ }
637
+ // Custom display name - resolve to an ID rather than subscribing to a guessed path.
638
+ return await this.findSystemFolder(client, mailboxId, folderName);
639
+ }
640
+ /**
641
+ * Creates a Microsoft Graph change-notification subscription for messages in a
642
+ * mailbox folder. Graph validates the notification endpoint synchronously during
643
+ * this call (it must echo the validation token), so an unreachable/incorrect endpoint
644
+ * surfaces here as a Graph error.
645
+ *
646
+ * @requires MS Graph Scope: Mail.Read (Application)
647
+ * @param params - What to watch, where to notify, and the clientState secret
648
+ * @param credentials - Optional credentials override for this request
649
+ * @returns Promise<SubscriptionResult> - Subscription ID and expiration on success
650
+ */
651
+ async CreateSubscription(params, credentials) {
652
+ // Fail-fast input validation (before any Graph call).
653
+ if (!params.ClientState) {
654
+ return { Success: false, ErrorMessage: 'CreateSubscription requires a non-empty ClientState' };
655
+ }
656
+ if (params.ClientState.length > 128) {
657
+ return { Success: false, ErrorMessage: `ClientState exceeds Microsoft Graph's 128-character limit (got ${params.ClientState.length})` };
658
+ }
659
+ if (!params.NotificationUrl || !params.NotificationUrl.toLowerCase().startsWith('https://')) {
660
+ return { Success: false, ErrorMessage: 'NotificationUrl must be an https:// URL' };
661
+ }
662
+ if (params.LifecycleNotificationUrl && !params.LifecycleNotificationUrl.toLowerCase().startsWith('https://')) {
663
+ return { Success: false, ErrorMessage: 'LifecycleNotificationUrl must be an https:// URL' };
664
+ }
665
+ if (!params.ChangeTypes || params.ChangeTypes.length === 0) {
666
+ return { Success: false, ErrorMessage: 'CreateSubscription requires at least one ChangeType' };
667
+ }
668
+ try {
669
+ const creds = this.resolveCredentials(credentials);
670
+ const client = this.getGraphClient(creds);
671
+ const mailboxId = params.Identifier || creds.accountEmail;
672
+ const context = params.ContextData;
673
+ const folderSegment = await this.resolveSubscriptionFolderSegment(client, mailboxId, context);
674
+ if (!folderSegment) {
675
+ return {
676
+ Success: false,
677
+ ErrorMessage: `Could not resolve folder '${context?.folderName}' for mailbox '${mailboxId}'`
678
+ };
679
+ }
680
+ const body = {
681
+ changeType: params.ChangeTypes.join(','),
682
+ notificationUrl: params.NotificationUrl,
683
+ resource: `/users/${mailboxId}/mailFolders('${folderSegment}')/messages`,
684
+ clientState: params.ClientState,
685
+ expirationDateTime: this.clampExpiration(params.RequestedExpiration)
686
+ };
687
+ if (params.LifecycleNotificationUrl) {
688
+ body.lifecycleNotificationUrl = params.LifecycleNotificationUrl;
689
+ }
690
+ const resp = await client.api(this.getSubscriptionsUri()).post(body);
691
+ return {
692
+ Success: true,
693
+ SubscriptionID: resp?.id,
694
+ ExpiresAt: resp?.expirationDateTime ? new Date(resp.expirationDateTime) : undefined,
695
+ Result: resp
696
+ };
697
+ }
698
+ catch (ex) {
699
+ LogError('Error creating subscription via MS Graph', undefined, ex);
700
+ return {
701
+ Success: false,
702
+ ErrorMessage: `Error creating subscription: ${ex instanceof Error ? ex.message : String(ex)}`
703
+ };
704
+ }
705
+ }
706
+ /**
707
+ * Renews an existing Graph subscription before it expires. The caller MUST pass
708
+ * credentials for the same app registration that created the subscription - Graph
709
+ * subscriptions are visible only to their creator, so a credential mismatch surfaces
710
+ * as a 404.
711
+ *
712
+ * @requires MS Graph Scope: Mail.Read (Application)
713
+ * @param params - The subscription ID and requested new expiration
714
+ * @param credentials - Optional credentials override for this request
715
+ * @returns Promise<SubscriptionResult> - The renewed expiration on success
716
+ */
717
+ async RenewSubscription(params, credentials) {
718
+ try {
719
+ const creds = this.resolveCredentials(credentials);
720
+ const client = this.getGraphClient(creds);
721
+ const resp = await client.api(`${this.getSubscriptionsUri()}/${params.SubscriptionID}`).patch({
722
+ expirationDateTime: this.clampExpiration(params.RequestedExpiration)
723
+ });
724
+ return {
725
+ Success: true,
726
+ SubscriptionID: resp?.id ?? params.SubscriptionID,
727
+ ExpiresAt: resp?.expirationDateTime ? new Date(resp.expirationDateTime) : undefined,
728
+ Result: resp
729
+ };
730
+ }
731
+ catch (ex) {
732
+ if (this.getGraphStatusCode(ex) === 404) {
733
+ LogError(`Subscription '${params.SubscriptionID}' not found during renewal - expired or created under different credentials`);
734
+ return {
735
+ Success: false,
736
+ ErrorMessage: 'subscription not found - expired or created under different credentials'
737
+ };
738
+ }
739
+ LogError('Error renewing subscription via MS Graph', undefined, ex);
740
+ return {
741
+ Success: false,
742
+ ErrorMessage: `Error renewing subscription: ${ex instanceof Error ? ex.message : String(ex)}`
743
+ };
744
+ }
745
+ }
746
+ /**
747
+ * Deletes an existing Graph subscription. Idempotent from the consumer's perspective:
748
+ * a 404 (already gone) is treated as success. The caller MUST pass credentials for
749
+ * the same app registration that created the subscription.
750
+ *
751
+ * @requires MS Graph Scope: Mail.Read (Application)
752
+ * @param params - The subscription ID to delete
753
+ * @param credentials - Optional credentials override for this request
754
+ * @returns Promise<BaseMessageResult> - Result of the delete operation
755
+ */
756
+ async DeleteSubscription(params, credentials) {
757
+ try {
758
+ const creds = this.resolveCredentials(credentials);
759
+ const client = this.getGraphClient(creds);
760
+ await client.api(`${this.getSubscriptionsUri()}/${params.SubscriptionID}`).delete();
761
+ return { Success: true };
762
+ }
763
+ catch (ex) {
764
+ if (this.getGraphStatusCode(ex) === 404) {
765
+ // Already gone - deletion is idempotent from the consumer's perspective.
766
+ return { Success: true };
767
+ }
768
+ LogError('Error deleting subscription via MS Graph', undefined, ex);
769
+ return {
770
+ Success: false,
771
+ ErrorMessage: `Error deleting subscription: ${ex instanceof Error ? ex.message : String(ex)}`
772
+ };
773
+ }
774
+ }
775
+ /**
776
+ * Parses and validates an inbound Graph change notification. Pure: no Graph client,
777
+ * no network. Safe on hostile/garbage input - never throws; returns `Success: false`
778
+ * with a 400 suggested status on malformed payloads.
779
+ *
780
+ * Graph has no cryptographic signature scheme, so `SignatureValid` is left undefined;
781
+ * the consumer authenticates each notification by comparing its `ClientState` against
782
+ * the secret stored alongside the subscription.
783
+ *
784
+ * @param input - Transport-neutral capture of the inbound webhook request
785
+ * @returns Promise<ParseNotificationResult> - Handshake or normalized notifications
786
+ */
787
+ async ParseNotification(input, _credentials) {
788
+ // 1. Endpoint-validation handshake. QueryParams are already framework-decoded per
789
+ // the WebhookNotificationInput contract - echo the token verbatim.
790
+ const validationToken = input?.QueryParams?.['validationToken'];
791
+ if (validationToken !== undefined) {
792
+ return {
793
+ Success: true,
794
+ Handshake: {
795
+ ResponseStatus: 200,
796
+ ResponseBody: validationToken,
797
+ ResponseContentType: 'text/plain'
798
+ },
799
+ Notifications: [],
800
+ SuggestedResponseStatus: 200
801
+ };
802
+ }
803
+ // 2. Parse the notification batch. Never throw on bad input.
804
+ let payload;
805
+ try {
806
+ payload = JSON.parse(input?.RawBody ?? '');
807
+ }
808
+ catch {
809
+ return {
810
+ Success: false,
811
+ ErrorMessage: 'Malformed notification body (invalid JSON)',
812
+ Notifications: [],
813
+ SuggestedResponseStatus: 400
814
+ };
815
+ }
816
+ const items = payload?.value;
817
+ if (!Array.isArray(items)) {
818
+ return {
819
+ Success: false,
820
+ ErrorMessage: 'Notification body missing value[] array',
821
+ Notifications: [],
822
+ SuggestedResponseStatus: 400
823
+ };
824
+ }
825
+ const notifications = items.map((raw) => {
826
+ const item = (raw ?? {});
827
+ const lifecycleEvent = item['lifecycleEvent'];
828
+ const resourceData = item['resourceData'];
829
+ const messageId = typeof resourceData?.id === 'string' ? resourceData.id : undefined;
830
+ return {
831
+ Kind: lifecycleEvent != null ? 'lifecycle' : 'message',
832
+ SubscriptionID: typeof item['subscriptionId'] === 'string' ? item['subscriptionId'] : undefined,
833
+ ClientState: typeof item['clientState'] === 'string' ? item['clientState'] : undefined,
834
+ Identifier: this.parseIdentifierFromResource(item['resource']),
835
+ ChangeType: this.mapChangeType(item['changeType']),
836
+ MessageIDs: messageId ? [messageId] : [],
837
+ LifecycleEvent: this.mapLifecycleEvent(lifecycleEvent),
838
+ RawData: raw
839
+ };
840
+ });
841
+ return {
842
+ Success: true,
843
+ SignatureValid: undefined,
844
+ Notifications: notifications,
845
+ SuggestedResponseStatus: 202
846
+ };
847
+ }
848
+ /**
849
+ * Returns MS Graph's subscription capabilities. Graph mail subscriptions last at most
850
+ * 4230 minutes (~3 days), support all three change types, and require synchronous
851
+ * endpoint validation at create time.
852
+ */
853
+ GetSubscriptionCapabilities() {
854
+ return {
855
+ MaxLifetimeMinutes: MSGRAPH_MAX_SUBSCRIPTION_MINUTES,
856
+ SupportedChangeTypes: ['created', 'updated', 'deleted'],
857
+ RequiresEndpointValidation: true,
858
+ // Graph is subscription-managed (CreateSubscription/RenewSubscription/DeleteSubscription)
859
+ // and HINT-mode: notifications carry resourceData IDs, the consumer re-fetches via
860
+ // GetMessages/GetSingleMessage — the payload is never delivered inline.
861
+ SupportsSubscriptionManagement: true,
862
+ DeliversPayloadInline: false
863
+ };
864
+ }
574
865
  /**
575
866
  * Gets a single message by ID from MS Graph.
576
867
  *
@@ -597,6 +888,8 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
597
888
  Message: {
598
889
  From: msgTyped.from?.emailAddress?.address || '',
599
890
  To: replyTo.length > 0 ? replyTo[0] : '',
891
+ ToRecipients: this.extractRecipientAddresses(msgTyped.toRecipients),
892
+ CCRecipients: this.extractRecipientAddresses(msgTyped.ccRecipients),
600
893
  ReplyTo: replyTo,
601
894
  Subject: msgTyped.subject || '',
602
895
  Body: msgTyped.body?.content || '',
@@ -850,6 +1143,8 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
850
1143
  const messages = response.value.map((msg) => ({
851
1144
  From: msg.from?.emailAddress?.address || '',
852
1145
  To: msg.toRecipients?.[0]?.emailAddress?.address || '',
1146
+ ToRecipients: this.extractRecipientAddresses(msg.toRecipients),
1147
+ CCRecipients: this.extractRecipientAddresses(msg.ccRecipients),
853
1148
  Subject: msg.subject || '',
854
1149
  Body: msg.bodyPreview || '',
855
1150
  ExternalSystemRecordID: msg.id || '',
@@ -962,19 +1257,8 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
962
1257
  */
963
1258
  async findSystemFolder(client, emailAddress, folderName) {
964
1259
  try {
965
- // MS Graph well-known folder names
966
- const wellKnownNames = {
967
- 'inbox': 'inbox',
968
- 'sent': 'sentitems',
969
- 'drafts': 'drafts',
970
- 'deleteditems': 'deleteditems',
971
- 'trash': 'deleteditems',
972
- 'junkemail': 'junkemail',
973
- 'spam': 'junkemail',
974
- 'archive': 'archive'
975
- };
976
- const normalizedName = folderName.toLowerCase();
977
- const graphFolderName = wellKnownNames[normalizedName] || normalizedName;
1260
+ // MS Graph well-known folder names (shared with the subscription resolver)
1261
+ const graphFolderName = this.resolveWellKnownFolder(folderName) || folderName.toLowerCase();
978
1262
  // Try well-known folder endpoint first - use email address directly
979
1263
  const folderPath = `${this.getApiUri()}/${encodeURIComponent(emailAddress)}/mailFolders/${graphFolderName}`;
980
1264
  const folder = await client.api(folderPath).get();
@@ -1042,6 +1326,82 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
1042
1326
  };
1043
1327
  return mapping[displayName.toLowerCase()] || 'other';
1044
1328
  }
1329
+ /**
1330
+ * Resolves a folder name to its Microsoft Graph well-known name when it is one,
1331
+ * matching case-insensitively against both friendly aliases and the canonical Graph
1332
+ * names. Graph accepts a well-known name directly in a resource path (no ID-resolution
1333
+ * call needed). Returns undefined for a custom display name, which then requires a
1334
+ * lookup. Shared by findSystemFolder and the CreateSubscription resource builder.
1335
+ */
1336
+ resolveWellKnownFolder(folderName) {
1337
+ const wellKnownNames = {
1338
+ 'inbox': 'inbox',
1339
+ 'sent': 'sentitems',
1340
+ 'sentitems': 'sentitems',
1341
+ 'drafts': 'drafts',
1342
+ 'deleteditems': 'deleteditems',
1343
+ 'trash': 'deleteditems',
1344
+ 'junkemail': 'junkemail',
1345
+ 'spam': 'junkemail',
1346
+ 'archive': 'archive'
1347
+ };
1348
+ return wellKnownNames[folderName.toLowerCase()];
1349
+ }
1350
+ /**
1351
+ * Extracts an HTTP status code from a thrown Microsoft Graph client error. The
1352
+ * middleware client throws a GraphError-shaped object carrying a numeric statusCode;
1353
+ * this narrows an untyped catch value to that code (or undefined when absent).
1354
+ */
1355
+ getGraphStatusCode(ex) {
1356
+ if (ex && typeof ex === 'object' && 'statusCode' in ex) {
1357
+ const sc = ex.statusCode;
1358
+ return typeof sc === 'number' ? sc : undefined;
1359
+ }
1360
+ return undefined;
1361
+ }
1362
+ /**
1363
+ * Parses the mailbox identifier (user ID / email) out of a Graph change-notification
1364
+ * resource string. Tolerant of both Users/{id}/Messages/{msgId} and
1365
+ * users/{id}/mailFolders('...')/messages/{msgId} shapes, case-insensitively. Returns
1366
+ * undefined when no pattern matches - the identifier is a routing convenience only.
1367
+ */
1368
+ parseIdentifierFromResource(resource) {
1369
+ if (typeof resource !== 'string') {
1370
+ return undefined;
1371
+ }
1372
+ const match = resource.match(/users\/([^/]+)/i);
1373
+ return match ? match[1] : undefined;
1374
+ }
1375
+ /**
1376
+ * Maps a raw Graph changeType value onto the normalized SubscriptionChangeType union,
1377
+ * returning undefined for anything unrecognized.
1378
+ */
1379
+ mapChangeType(raw) {
1380
+ if (raw === 'created' || raw === 'updated' || raw === 'deleted') {
1381
+ return raw;
1382
+ }
1383
+ return undefined;
1384
+ }
1385
+ /**
1386
+ * Maps a raw Graph lifecycleEvent value onto the normalized lifecycle-event union,
1387
+ * returning undefined for anything unrecognized.
1388
+ */
1389
+ mapLifecycleEvent(raw) {
1390
+ if (raw === 'subscriptionRemoved' || raw === 'missed' || raw === 'reauthorizationRequired') {
1391
+ return raw;
1392
+ }
1393
+ return undefined;
1394
+ }
1395
+ /**
1396
+ * Clamps a requested subscription expiration to Graph's maximum lifetime, returning an
1397
+ * ISO-8601 UTC string. When no expiration is requested (or it exceeds the max), the
1398
+ * maximum-allowed expiration from now is used.
1399
+ */
1400
+ clampExpiration(requested) {
1401
+ const max = new Date(Date.now() + MSGRAPH_MAX_SUBSCRIPTION_MINUTES * 60 * 1000);
1402
+ const effective = requested && requested.getTime() < max.getTime() ? requested : max;
1403
+ return effective.toISOString();
1404
+ }
1045
1405
  };
1046
1406
  MSGraphProvider = __decorate([
1047
1407
  RegisterClass(BaseCommunicationProvider, 'Microsoft Graph'),