@memberjunction/communication-ms-graph 6.1.0-edge.5 → 6.1.0-edge.7

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.
package/README.md CHANGED
@@ -60,11 +60,17 @@ AZURE_ACCOUNT_EMAIL=mailbox@yourdomain.com
60
60
  | `Mail.Send` | SendSingleMessage, ForwardMessage, ReplyToMessage |
61
61
  | `Mail.Read` | GetMessages, GetSingleMessage, SearchMessages, ListFolders, ListAttachments, DownloadAttachment |
62
62
  | `Mail.ReadWrite` | CreateDraft, DeleteMessage, MoveMessage, MarkAsRead, ArchiveMessage |
63
+ | `Calendars.Read` | GetEvents |
63
64
  | `User.Read.All` | GetServiceAccount (user lookup, optional) |
64
65
 
66
+ `Calendars.Read` is sufficient for `GetEvents`; `Calendars.ReadWrite` is not required, since nothing
67
+ here creates or modifies an event. As **Application** permissions these are granted against the
68
+ tenant, not against one mailbox — narrowing an app to specific mailboxes is done in Exchange with
69
+ RBAC for Applications, not by these grants.
70
+
65
71
  ## Supported Operations
66
72
 
67
- This provider supports all 14 operations defined in `BaseCommunicationProvider`:
73
+ This provider supports all 15 operations defined in `BaseCommunicationProvider`:
68
74
 
69
75
  | Operation | Description |
70
76
  |-----------|-------------|
@@ -82,6 +88,7 @@ This provider supports all 14 operations defined in `BaseCommunicationProvider`:
82
88
  | `SearchMessages` | Full-text search with KQL syntax and date filtering |
83
89
  | `ListAttachments` | List attachments on a message |
84
90
  | `DownloadAttachment` | Download attachment content as base64/Buffer |
91
+ | `GetEvents` | Read calendar events for one mailbox |
85
92
 
86
93
  ## Usage
87
94
 
@@ -151,6 +158,39 @@ result.Messages.forEach(msg => {
151
158
  });
152
159
  ```
153
160
 
161
+ ### Reading Calendar Events
162
+
163
+ Requires `Calendars.Read`. **Whether you pass a window changes what comes back**, so `GetEvents`
164
+ reports which it did via `RecurrenceExpanded` rather than leaving you to guess:
165
+
166
+ ```typescript
167
+ const provider = engine.GetProvider('Microsoft Graph');
168
+
169
+ // With a window -> /calendarView: a recurring series is EXPANDED into one entry per occurrence.
170
+ const occurrences = await provider.GetEvents({
171
+ Identifier: 'rep@example.com',
172
+ NumEvents: 50,
173
+ StartDateTime: new Date('2026-09-01T00:00:00Z'),
174
+ EndDateTime: new Date('2026-09-08T00:00:00Z')
175
+ });
176
+ console.log(occurrences.RecurrenceExpanded); // true
177
+
178
+ // Without one -> /events: a weekly stand-up is ONE row, the series master, whose start time is
179
+ // whenever the series began — possibly years ago.
180
+ const masters = await provider.GetEvents({ Identifier: 'rep@example.com', NumEvents: 50 });
181
+ console.log(masters.RecurrenceExpanded); // false
182
+ ```
183
+
184
+ Three things worth knowing before syncing on this:
185
+
186
+ - **The window selects overlap, not start times.** An event that began before `StartDateTime` and is
187
+ still running when the window opens is returned. An event straddling a boundary therefore appears
188
+ in both adjacent windows — dedupe on the event id.
189
+ - **`NumEvents` is one page.** It becomes `$top`; there is no `@odata.nextLink` following. To cover a
190
+ period completely, narrow the window rather than raise the number.
191
+ - **Cancelled events are excluded by default** and cannot be recovered after the fact — Graph does not
192
+ return them once filtered. Pass `IncludeCancelled: true` if you are logging history.
193
+
154
194
  ### Searching Messages
155
195
 
156
196
  MS Graph supports KQL (Keyword Query Language) for search:
@@ -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, CreateSubscriptionParams, RenewSubscriptionParams, DeleteSubscriptionParams, SubscriptionResult, SubscriptionCapabilities, WebhookNotificationInput, ParseNotificationResult, BaseMessageResult } from "@memberjunction/communication-types";
1
+ import { BaseCommunicationProvider, MessageRetrievalCapabilities, CreateDraftParams, CreateDraftResult, ForwardMessageParams, ForwardMessageResult, GetEventsParams, GetEventsResult, 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
  /**
@@ -78,6 +78,11 @@ export interface MSGraphCredentials extends ProviderCredentialsBase {
78
78
  * ```
79
79
  */
80
80
  export declare class MSGraphProvider extends BaseCommunicationProvider {
81
+ /**
82
+ * Graph filters both of these server-side, so neither is emulated here — `receivedDateTime`
83
+ * comparisons and `isRead` are ordinary `$filter` clauses.
84
+ */
85
+ get MessageRetrieval(): MessageRetrievalCapabilities;
81
86
  private HTMLConverter;
82
87
  /**
83
88
  * Cache clients keyed by tenant + clientId. Bounded LRU(100) + 1-hour TTL —
@@ -90,6 +95,18 @@ export declare class MSGraphProvider extends BaseCommunicationProvider {
90
95
  * Resolves MS Graph credentials from request and environment.
91
96
  */
92
97
  private resolveCredentials;
98
+ /**
99
+ * The mailbox an operation will act on, or a refusal that says how to supply one.
100
+ *
101
+ * WHY THIS THROWS. Every caller interpolates the result into a Graph path. Returning `undefined`
102
+ * would put the literal string "undefined" in the URL and come back as a 404 that reads like
103
+ * "message not found" — a wrong answer that looks like a real one. This package does not enable
104
+ * `strictNullChecks`, so the compiler would not have caught that either.
105
+ *
106
+ * `credentials.accountEmail` is the LAST candidate on purpose: it is a default for the deployment,
107
+ * and anything the caller named for this specific request outranks it.
108
+ */
109
+ private resolveMailbox;
93
110
  /**
94
111
  * Gets or creates a Graph client for the given credentials.
95
112
  * Uses cached client if credentials match environment (default case).
@@ -208,6 +225,40 @@ export declare class MSGraphProvider extends BaseCommunicationProvider {
208
225
  * @requires MS Graph Scope: Mail.ReadWrite (Application)
209
226
  */
210
227
  CreateDraft(params: CreateDraftParams, credentials?: MSGraphCredentials): Promise<CreateDraftResult>;
228
+ /**
229
+ * Reads calendar events for one mailbox.
230
+ *
231
+ * @requires MS Graph Scope: Calendars.Read (Application)
232
+ *
233
+ * TWO ENDPOINTS, AND THE CHOICE IS VISIBLE TO THE CALLER. Graph exposes calendar data two ways
234
+ * and they do not return the same thing:
235
+ *
236
+ * - `/calendarView` REQUIRES a start and end and EXPANDS recurring series into one entry per
237
+ * occurrence. A weekly stand-up in a two-week window comes back as two events, each with its
238
+ * own start time and id.
239
+ * - `/events` needs no window and does NOT expand. That same stand-up is one row, the series
240
+ * master, whose start time is whenever the series began - possibly years ago.
241
+ *
242
+ * A caller logging what actually happened wants occurrences; one asking "what meetings exist"
243
+ * may want masters. Silently picking would be a trap, because the two are indistinguishable by
244
+ * inspection - so the window decides, and `RecurrenceExpanded` on the result REPORTS which
245
+ * happened rather than leaving the caller to infer it.
246
+ *
247
+ * CANCELLED EVENTS ARE EXCLUDED BY DEFAULT and cannot be recovered afterwards - Graph does not
248
+ * return them once filtered - so `IncludeCancelled` is honoured before the `$top` cap rather
249
+ * than by discarding rows after the fetch, which would silently shrink the page.
250
+ */
251
+ GetEvents(params: GetEventsParams, credentials?: MSGraphCredentials): Promise<GetEventsResult>;
252
+ /**
253
+ * Graph event -> the normalized shape. Lossy by design; `SourceData` keeps the original.
254
+ *
255
+ * A start Graph cannot express as an instant becomes NULL rather than a guess. Graph returns
256
+ * naive local strings plus a separate timeZone, so a value with neither a zone suffix nor a UTC
257
+ * marker genuinely has no instant here, and inventing one would file a meeting at the wrong time.
258
+ */
259
+ private toGetEventsEvent;
260
+ /** A Graph date slot as an instant, or null when it does not determine one. */
261
+ private static graphInstant;
211
262
  /**
212
263
  * Returns the list of operations supported by MS Graph provider.
213
264
  * MS Graph supports all mailbox operations.
@@ -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":"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"}
@@ -7,7 +7,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
- import { BaseCommunicationProvider, resolveCredentialValue, validateRequiredCredentials } from "@memberjunction/communication-types";
10
+ var MSGraphProvider_1;
11
+ import { BaseCommunicationProvider, CombineFilterClauses, resolveCredentialValue, validateRequiredCredentials } from "@memberjunction/communication-types";
11
12
  import { Client } from '@microsoft/microsoft-graph-client';
12
13
  import { ClientSecretCredential } from '@azure/identity';
13
14
  import { TokenCredentialAuthenticationProvider } from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials/index.js";
@@ -20,6 +21,16 @@ import * as Config from "./config.js";
20
21
  * The maximum lifetime, in minutes, that Microsoft Graph allows for a mail-resource
21
22
  * change-notification subscription (~3 days). Requested expirations are clamped to this.
22
23
  */
24
+ /**
25
+ * Format an instant the way an OData `$filter` comparison wants it: ISO-8601, UTC, UNQUOTED.
26
+ *
27
+ * Quoting it makes Graph compare a datetime against a string and reject the request, which is the
28
+ * mistake this exists to keep out of the call sites. `toISOString` is always UTC with a trailing Z
29
+ * regardless of the Date's origin, so a caller passing a local-time Date still filters correctly.
30
+ */
31
+ function ODataInstant(when) {
32
+ return when.toISOString();
33
+ }
23
34
  const MSGRAPH_MAX_SUBSCRIPTION_MINUTES = 4230;
24
35
  /**
25
36
  * Implementation of the MS Graph provider for sending and receiving messages.
@@ -46,7 +57,14 @@ const MSGRAPH_MAX_SUBSCRIPTION_MINUTES = 4230;
46
57
  * });
47
58
  * ```
48
59
  */
49
- let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
60
+ let MSGraphProvider = MSGraphProvider_1 = class MSGraphProvider extends BaseCommunicationProvider {
61
+ /**
62
+ * Graph filters both of these server-side, so neither is emulated here — `receivedDateTime`
63
+ * comparisons and `isRead` are ordinary `$filter` clauses.
64
+ */
65
+ get MessageRetrieval() {
66
+ return { FilterByReceivedDate: true, FilterByUnread: true };
67
+ }
50
68
  constructor() {
51
69
  super();
52
70
  /**
@@ -71,7 +89,12 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
71
89
  const clientId = resolveCredentialValue(credentials?.clientId, Config.AZURE_CLIENT_ID, disableFallback);
72
90
  const clientSecret = resolveCredentialValue(credentials?.clientSecret, Config.AZURE_CLIENT_SECRET, disableFallback);
73
91
  const accountEmail = resolveCredentialValue(credentials?.accountEmail, Config.AZURE_ACCOUNT_EMAIL, disableFallback);
74
- validateRequiredCredentials({ tenantId, clientId, clientSecret, accountEmail }, ['tenantId', 'clientId', 'clientSecret', 'accountEmail'], 'Microsoft Graph');
92
+ // The THREE authentication fields, which is exactly what a service principal is and exactly
93
+ // what the `Azure Service Principal` credential type declares. `accountEmail` used to be
94
+ // required here, which made every stored credential of that type unusable: there was no
95
+ // fourth field for an operator to fill in, so every operation failed before doing anything.
96
+ // A mailbox is resolved per operation instead — see `resolveMailbox`.
97
+ validateRequiredCredentials({ tenantId, clientId, clientSecret }, ['tenantId', 'clientId', 'clientSecret'], 'Microsoft Graph');
75
98
  return {
76
99
  tenantId: tenantId,
77
100
  clientId: clientId,
@@ -79,6 +102,25 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
79
102
  accountEmail: accountEmail
80
103
  };
81
104
  }
105
+ /**
106
+ * The mailbox an operation will act on, or a refusal that says how to supply one.
107
+ *
108
+ * WHY THIS THROWS. Every caller interpolates the result into a Graph path. Returning `undefined`
109
+ * would put the literal string "undefined" in the URL and come back as a 404 that reads like
110
+ * "message not found" — a wrong answer that looks like a real one. This package does not enable
111
+ * `strictNullChecks`, so the compiler would not have caught that either.
112
+ *
113
+ * `credentials.accountEmail` is the LAST candidate on purpose: it is a default for the deployment,
114
+ * and anything the caller named for this specific request outranks it.
115
+ */
116
+ resolveMailbox(operation, creds, ...preferred) {
117
+ for (const candidate of [...preferred, creds.accountEmail]) {
118
+ if (candidate && candidate.trim() !== '')
119
+ return candidate.trim();
120
+ }
121
+ throw new Error(`Microsoft Graph: ${operation} needs a mailbox and none was supplied. Pass one on the ` +
122
+ `request, or set accountEmail on the credential (or AZURE_ACCOUNT_EMAIL) as a default.`);
123
+ }
82
124
  /**
83
125
  * Gets or creates a Graph client for the given credentials.
84
126
  * Uses cached client if credentials match environment (default case).
@@ -129,12 +171,9 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
129
171
  const creds = this.resolveCredentials(credentials);
130
172
  const client = this.getGraphClient(creds);
131
173
  // Smart selection: use message.From if provided and different from resolved accountEmail
132
- let senderEmail = creds.accountEmail;
133
- if (message.From &&
134
- message.From.trim() !== '' &&
135
- message.From !== creds.accountEmail) {
136
- senderEmail = message.From;
137
- }
174
+ // Whatever the caller named outranks the credential default; a request that names
175
+ // neither is refused here rather than sending from "undefined".
176
+ const senderEmail = this.resolveMailbox('SendSingleMessage', creds, message.From);
138
177
  if (!message) {
139
178
  return {
140
179
  Message: message,
@@ -237,8 +276,13 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
237
276
  },
238
277
  comment: params.Message.ProcessedBody || params.Message.ProcessedHTMLBody
239
278
  };
240
- // Use email address directly in API path
241
- const sendMessagePath = `${this.getApiUri()}/${encodeURIComponent(creds.accountEmail)}/messages/${params.MessageID}/reply`;
279
+ // `ContextData.Email` FIRST, as eleven sibling operations already read it. Without it this
280
+ // site could only resolve `creds.accountEmail` — which the `Azure Service Principal`
281
+ // credential type declares no property for, and which `disableEnvironmentFallback` removes
282
+ // the environment source for. Reply was therefore unreachable on exactly the stored
283
+ // credential the mailbox rework exists to support.
284
+ const mailbox = this.resolveMailbox('ReplyToMessage', creds, params.ContextData?.Email);
285
+ const sendMessagePath = `${this.getApiUri()}/${encodeURIComponent(mailbox)}/messages/${params.MessageID}/reply`;
242
286
  const result = await client.api(sendMessagePath).post(reply);
243
287
  return {
244
288
  Success: true,
@@ -249,7 +293,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
249
293
  LogError(ex);
250
294
  return {
251
295
  Success: false,
252
- ErrorMessage: 'Error sending message'
296
+ ErrorMessage: `Error sending message: ${ex instanceof Error ? ex.message : String(ex)}`
253
297
  };
254
298
  }
255
299
  }
@@ -262,15 +306,29 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
262
306
  const creds = this.resolveCredentials(credentials);
263
307
  const client = this.getGraphClient(creds);
264
308
  const contextData = params.ContextData;
265
- const emailToUse = params.Identifier || contextData?.Email || creds.accountEmail;
266
- let filter = "";
309
+ const emailToUse = this.resolveMailbox('GetMessages', creds, params.Identifier, contextData?.Email);
267
310
  const top = params.NumMessages;
311
+ const applied = { ReceivedAfter: false, ReceivedBefore: false, UnreadOnly: false };
312
+ const clauses = [];
268
313
  if (params.UnreadOnly) {
269
- filter = "(isRead eq false)";
314
+ clauses.push("(isRead eq false)");
315
+ applied.UnreadOnly = true;
270
316
  }
317
+ if (params.ReceivedAfter) {
318
+ clauses.push(`(receivedDateTime ge ${ODataInstant(params.ReceivedAfter)})`);
319
+ applied.ReceivedAfter = true;
320
+ }
321
+ if (params.ReceivedBefore) {
322
+ clauses.push(`(receivedDateTime le ${ODataInstant(params.ReceivedBefore)})`);
323
+ applied.ReceivedBefore = true;
324
+ }
325
+ // COMPOSED, not assigned. This branch used to overwrite `filter` outright, so a caller that
326
+ // passed UnreadOnly together with a ContextData.Filter silently lost the UnreadOnly clause
327
+ // and got read mail back. Anything the caller supplies here now narrows alongside the rest.
271
328
  if (contextData && contextData.Filter) {
272
- filter = contextData.Filter;
329
+ clauses.push(String(contextData.Filter));
273
330
  }
331
+ const filter = CombineFilterClauses(clauses, " and ");
274
332
  // Use email address directly in API path
275
333
  const messagesPath = `${this.getApiUri()}/${encodeURIComponent(emailToUse)}/messages`;
276
334
  const response = await client.api(messagesPath)
@@ -285,7 +343,8 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
285
343
  const messageResults = {
286
344
  Success: true,
287
345
  SourceData: sourceMessages,
288
- Messages: []
346
+ Messages: [],
347
+ AppliedFilters: applied
289
348
  };
290
349
  let headers = null;
291
350
  // GetHeaders is an async function for one specific message. I need
@@ -360,7 +419,8 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
360
419
  }))
361
420
  };
362
421
  // Use email address directly in API path
363
- const sendMessagePath = `${this.getApiUri()}/${encodeURIComponent(creds.accountEmail)}/messages/${params.MessageID}/forward`;
422
+ const mailbox = this.resolveMailbox('ForwardMessage', creds);
423
+ const sendMessagePath = `${this.getApiUri()}/${encodeURIComponent(mailbox)}/messages/${params.MessageID}/forward`;
364
424
  const forwardResult = await client.api(sendMessagePath).post(forward);
365
425
  return {
366
426
  Success: true,
@@ -370,7 +430,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
370
430
  catch (ex) {
371
431
  LogError(ex);
372
432
  return {
373
- ErrorMessage: 'An Error occurred while forwarding the message',
433
+ ErrorMessage: `An Error occurred while forwarding the message: ${ex instanceof Error ? ex.message : String(ex)}`,
374
434
  Success: false
375
435
  };
376
436
  }
@@ -523,12 +583,9 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
523
583
  const creds = this.resolveCredentials(credentials);
524
584
  const client = this.getGraphClient(creds);
525
585
  // Smart selection: use message.From if provided and different from resolved accountEmail
526
- let senderEmail = creds.accountEmail;
527
- if (params.Message.From &&
528
- params.Message.From.trim() !== '' &&
529
- params.Message.From !== creds.accountEmail) {
530
- senderEmail = params.Message.From;
531
- }
586
+ // Whatever the caller named outranks the credential default; a request that names
587
+ // neither is refused here rather than sending from "undefined".
588
+ const senderEmail = this.resolveMailbox('CreateDraft', creds, params.Message.From);
532
589
  // Build message object (similar to SendSingleMessage but saved as draft)
533
590
  const draftMessage = {
534
591
  subject: params.Message.ProcessedSubject,
@@ -568,13 +625,143 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
568
625
  LogError('Error creating draft via MS Graph', undefined, ex);
569
626
  return {
570
627
  Success: false,
571
- ErrorMessage: 'Error creating draft'
628
+ ErrorMessage: `Error creating draft: ${ex instanceof Error ? ex.message : String(ex)}`
572
629
  };
573
630
  }
574
631
  }
575
632
  // ========================================================================
576
633
  // EXTENDED OPERATIONS - MS Graph supports full mailbox access
577
634
  // ========================================================================
635
+ /**
636
+ * Reads calendar events for one mailbox.
637
+ *
638
+ * @requires MS Graph Scope: Calendars.Read (Application)
639
+ *
640
+ * TWO ENDPOINTS, AND THE CHOICE IS VISIBLE TO THE CALLER. Graph exposes calendar data two ways
641
+ * and they do not return the same thing:
642
+ *
643
+ * - `/calendarView` REQUIRES a start and end and EXPANDS recurring series into one entry per
644
+ * occurrence. A weekly stand-up in a two-week window comes back as two events, each with its
645
+ * own start time and id.
646
+ * - `/events` needs no window and does NOT expand. That same stand-up is one row, the series
647
+ * master, whose start time is whenever the series began - possibly years ago.
648
+ *
649
+ * A caller logging what actually happened wants occurrences; one asking "what meetings exist"
650
+ * may want masters. Silently picking would be a trap, because the two are indistinguishable by
651
+ * inspection - so the window decides, and `RecurrenceExpanded` on the result REPORTS which
652
+ * happened rather than leaving the caller to infer it.
653
+ *
654
+ * CANCELLED EVENTS ARE EXCLUDED BY DEFAULT and cannot be recovered afterwards - Graph does not
655
+ * return them once filtered - so `IncludeCancelled` is honoured before the `$top` cap rather
656
+ * than by discarding rows after the fetch, which would silently shrink the page.
657
+ */
658
+ async GetEvents(params, credentials) {
659
+ const creds = this.resolveCredentials(credentials);
660
+ const client = this.getGraphClient(creds);
661
+ const contextData = params.ContextData;
662
+ const mailbox = params.Identifier || contextData?.Email || creds.accountEmail;
663
+ if (!mailbox) {
664
+ return {
665
+ Success: false,
666
+ Events: [],
667
+ ErrorMessage: 'GetEvents needs an Identifier (mailbox) or credentials scoped to one.'
668
+ };
669
+ }
670
+ const expand = !!params.StartDateTime && !!params.EndDateTime;
671
+ const base = `${this.getApiUri()}/${encodeURIComponent(mailbox)}`;
672
+ try {
673
+ let request = expand
674
+ ? client
675
+ .api(`${base}/calendarView`)
676
+ .query({
677
+ startDateTime: params.StartDateTime.toISOString(),
678
+ endDateTime: params.EndDateTime.toISOString()
679
+ })
680
+ .orderby('start/dateTime')
681
+ : client.api(`${base}/events`).orderby('lastModifiedDateTime desc');
682
+ // Ask for UTC explicitly rather than relying on it. Graph returns start/end in UTC when no
683
+ // `Prefer: outlook.timezone` is sent, so this changes nothing today — but the default is
684
+ // Microsoft's to change, and every other value would need a tz database to resolve. Saying
685
+ // it makes the mapper's UTC assumption a request rather than a bet. `graphInstant` keeps
686
+ // its null fallback for the case where a non-UTC zone comes back anyway.
687
+ request = request.header('Prefer', 'outlook.timezone="UTC"');
688
+ // Applied server-side so the $top cap counts only events the caller asked for. Filtering
689
+ // after the fetch would return fewer than NumEvents and look like an empty calendar.
690
+ //
691
+ // $filter AND $orderby TOGETHER ARE ACCEPTED HERE. Outlook's backend requires every
692
+ // $orderby property to also appear in $filter for MESSAGES, returning 400
693
+ // `InefficientFilter` otherwise, and Microsoft's announcement of that rule is titled for
694
+ // Mail, Calendar and Contacts — so this combination looks like it should fail. It does
695
+ // not: both shapes below were sent against a live tenant and returned 200. Recorded here
696
+ // because the unit tests mock the Graph client and can never catch a 400, so the next
697
+ // reader has no way to re-derive it short of running the query again.
698
+ if (!params.IncludeCancelled) {
699
+ request = request.filter('isCancelled eq false');
700
+ }
701
+ const response = await request.top(params.NumEvents).get();
702
+ if (!response) {
703
+ return { Success: false, Events: [], ErrorMessage: 'Graph returned no response for the calendar read.' };
704
+ }
705
+ const sourceEvents = response.value ?? [];
706
+ return {
707
+ Success: true,
708
+ SourceData: sourceEvents,
709
+ RecurrenceExpanded: expand,
710
+ Events: sourceEvents.map((e) => this.toGetEventsEvent(e))
711
+ };
712
+ }
713
+ catch (err) {
714
+ // Surfaced, not swallowed into an empty list: a caller advancing a watermark must be able
715
+ // to tell "could not read" from "nothing scheduled".
716
+ return {
717
+ Success: false,
718
+ Events: [],
719
+ ErrorMessage: `Graph calendar read failed for ${mailbox}: ${err instanceof Error ? err.message : String(err)}`
720
+ };
721
+ }
722
+ }
723
+ /**
724
+ * Graph event -> the normalized shape. Lossy by design; `SourceData` keeps the original.
725
+ *
726
+ * A start Graph cannot express as an instant becomes NULL rather than a guess. Graph returns
727
+ * naive local strings plus a separate timeZone, so a value with neither a zone suffix nor a UTC
728
+ * marker genuinely has no instant here, and inventing one would file a meeting at the wrong time.
729
+ */
730
+ toGetEventsEvent(raw) {
731
+ const event = raw;
732
+ const organizer = event.organizer?.emailAddress?.address?.trim().toLowerCase() || undefined;
733
+ const attendees = (event.attendees ?? [])
734
+ .map((a) => a.emailAddress?.address?.trim().toLowerCase())
735
+ .filter((a) => !!a && a !== organizer);
736
+ return {
737
+ ExternalSystemRecordID: event.id ?? '',
738
+ SeriesID: event.seriesMasterId ?? null,
739
+ Subject: event.subject ?? '',
740
+ Body: event.bodyPreview ?? '',
741
+ StartTime: MSGraphProvider_1.graphInstant(event.start),
742
+ EndTime: MSGraphProvider_1.graphInstant(event.end),
743
+ Location: event.location?.displayName ?? null,
744
+ Organizer: organizer,
745
+ Attendees: [...new Set(attendees)],
746
+ IsCancelled: event.isCancelled === true
747
+ };
748
+ }
749
+ /** A Graph date slot as an instant, or null when it does not determine one. */
750
+ static graphInstant(slot) {
751
+ const raw = slot?.dateTime?.trim();
752
+ if (!raw)
753
+ return null;
754
+ // Both alternatives anchored. Unanchored, the `[Zz]` matched a "z" ANYWHERE in the string, so
755
+ // a value that merely contained one counted as carrying an offset and skipped the UTC check.
756
+ const hasOffset = /(?:[Zz]|[+-]\d{2}:?\d{2})$/.test(raw);
757
+ const zone = (slot?.timeZone ?? 'UTC').trim().toUpperCase();
758
+ // Only UTC is safe to assume. Any other named zone would need a tz database to resolve, and
759
+ // guessing puts the meeting hours away from when it happened.
760
+ if (!hasOffset && zone !== 'UTC')
761
+ return null;
762
+ const parsed = new Date(hasOffset ? raw : `${raw}Z`);
763
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
764
+ }
578
765
  /**
579
766
  * Returns the list of operations supported by MS Graph provider.
580
767
  * MS Graph supports all mailbox operations.
@@ -598,7 +785,8 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
598
785
  'CreateSubscription',
599
786
  'RenewSubscription',
600
787
  'DeleteSubscription',
601
- 'ParseNotification'
788
+ 'ParseNotification',
789
+ 'GetEvents'
602
790
  ];
603
791
  }
604
792
  // ========================================================================
@@ -668,7 +856,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
668
856
  try {
669
857
  const creds = this.resolveCredentials(credentials);
670
858
  const client = this.getGraphClient(creds);
671
- const mailboxId = params.Identifier || creds.accountEmail;
859
+ const mailboxId = this.resolveMailbox('CreateSubscription', creds, params.Identifier);
672
860
  const context = params.ContextData;
673
861
  const folderSegment = await this.resolveSubscriptionFolderSegment(client, mailboxId, context);
674
862
  if (!folderSegment) {
@@ -871,7 +1059,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
871
1059
  try {
872
1060
  const creds = this.resolveCredentials(credentials);
873
1061
  const client = this.getGraphClient(creds);
874
- const emailToUse = params.ContextData?.Email || creds.accountEmail;
1062
+ const emailToUse = this.resolveMailbox('GetSingleMessage', creds, params.ContextData?.Email);
875
1063
  // Use email address directly in API path
876
1064
  const messagePath = `${this.getApiUri()}/${encodeURIComponent(emailToUse)}/messages/${params.MessageID}`;
877
1065
  const msgResponse = await client.api(messagePath).get();
@@ -921,7 +1109,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
921
1109
  try {
922
1110
  const creds = this.resolveCredentials(credentials);
923
1111
  const client = this.getGraphClient(creds);
924
- const emailToUse = params.ContextData?.Email || creds.accountEmail;
1112
+ const emailToUse = this.resolveMailbox('DeleteMessage', creds, params.ContextData?.Email);
925
1113
  // Use email address directly in API path
926
1114
  const messagePath = `${this.getApiUri()}/${encodeURIComponent(emailToUse)}/messages/${params.MessageID}`;
927
1115
  if (params.PermanentDelete) {
@@ -961,7 +1149,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
961
1149
  try {
962
1150
  const creds = this.resolveCredentials(credentials);
963
1151
  const client = this.getGraphClient(creds);
964
- const emailToUse = params.ContextData?.Email || creds.accountEmail;
1152
+ const emailToUse = this.resolveMailbox('MoveMessage', creds, params.ContextData?.Email);
965
1153
  // Use email address directly in API path
966
1154
  const movePath = `${this.getApiUri()}/${encodeURIComponent(emailToUse)}/messages/${params.MessageID}/move`;
967
1155
  const result = await client.api(movePath).post({
@@ -991,7 +1179,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
991
1179
  try {
992
1180
  const creds = this.resolveCredentials(credentials);
993
1181
  const client = this.getGraphClient(creds);
994
- const emailToUse = params.ContextData?.Email || creds.accountEmail;
1182
+ const emailToUse = this.resolveMailbox('ListFolders', creds, params.ContextData?.Email);
995
1183
  // Use email address directly in API path
996
1184
  let foldersPath;
997
1185
  if (params.ParentFolderID) {
@@ -1039,7 +1227,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
1039
1227
  try {
1040
1228
  const creds = this.resolveCredentials(credentials);
1041
1229
  const client = this.getGraphClient(creds);
1042
- const emailToUse = params.ContextData?.Email || creds.accountEmail;
1230
+ const emailToUse = this.resolveMailbox('MarkAsRead', creds, params.ContextData?.Email);
1043
1231
  // Use email address directly in API path - update each message
1044
1232
  const updatePromises = params.MessageIDs.map(async (messageId) => {
1045
1233
  const updatePath = `${this.getApiUri()}/${encodeURIComponent(emailToUse)}/messages/${messageId}`;
@@ -1066,7 +1254,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
1066
1254
  try {
1067
1255
  const creds = this.resolveCredentials(credentials);
1068
1256
  const client = this.getGraphClient(creds);
1069
- const emailToUse = params.ContextData?.Email || creds.accountEmail;
1257
+ const emailToUse = this.resolveMailbox('ArchiveMessage', creds, params.ContextData?.Email);
1070
1258
  // Find or create the Archive folder - use email address directly
1071
1259
  let archiveFolderId = await this.findSystemFolder(client, emailToUse, 'archive');
1072
1260
  if (!archiveFolderId) {
@@ -1104,7 +1292,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
1104
1292
  try {
1105
1293
  const creds = this.resolveCredentials(credentials);
1106
1294
  const client = this.getGraphClient(creds);
1107
- const emailToUse = params.ContextData?.Email || creds.accountEmail;
1295
+ const emailToUse = this.resolveMailbox('SearchMessages', creds, params.ContextData?.Email);
1108
1296
  // Build search path - use email address directly in API path
1109
1297
  let messagesPath;
1110
1298
  if (params.FolderID) {
@@ -1176,7 +1364,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
1176
1364
  try {
1177
1365
  const creds = this.resolveCredentials(credentials);
1178
1366
  const client = this.getGraphClient(creds);
1179
- const emailToUse = params.ContextData?.Email || creds.accountEmail;
1367
+ const emailToUse = this.resolveMailbox('ListAttachments', creds, params.ContextData?.Email);
1180
1368
  // Use email address directly in API path
1181
1369
  const attachmentsPath = `${this.getApiUri()}/${encodeURIComponent(emailToUse)}/messages/${params.MessageID}/attachments`;
1182
1370
  const response = await client.api(attachmentsPath).get();
@@ -1217,7 +1405,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
1217
1405
  try {
1218
1406
  const creds = this.resolveCredentials(credentials);
1219
1407
  const client = this.getGraphClient(creds);
1220
- const emailToUse = params.ContextData?.Email || creds.accountEmail;
1408
+ const emailToUse = this.resolveMailbox('DownloadAttachment', creds, params.ContextData?.Email);
1221
1409
  // Use email address directly in API path
1222
1410
  const attachmentPath = `${this.getApiUri()}/${encodeURIComponent(emailToUse)}/messages/${params.MessageID}/attachments/${params.AttachmentID}`;
1223
1411
  const response = await client.api(attachmentPath).get();
@@ -1403,7 +1591,7 @@ let MSGraphProvider = class MSGraphProvider extends BaseCommunicationProvider {
1403
1591
  return effective.toISOString();
1404
1592
  }
1405
1593
  };
1406
- MSGraphProvider = __decorate([
1594
+ MSGraphProvider = MSGraphProvider_1 = __decorate([
1407
1595
  RegisterClass(BaseCommunicationProvider, 'Microsoft Graph'),
1408
1596
  __metadata("design:paramtypes", [])
1409
1597
  ], MSGraphProvider);