@deploybaseline/microsoft 0.0.0-stage → 0.14.1

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.
@@ -0,0 +1,4762 @@
1
+ // src/graph/handlers/upload.ts
2
+ import { randomBytes as randomBytes4, randomUUID as randomUUID3 } from "crypto";
3
+
4
+ // src/graph/errors.ts
5
+ var GraphError = class extends Error {
6
+ status;
7
+ code;
8
+ layer;
9
+ headers;
10
+ /** innerError.code (To Do service errors carry one). */
11
+ innerCode;
12
+ /** Extra members of the error object (annotations such as `@error.fixItUrl`). */
13
+ extra;
14
+ constructor(status, code, message, layer = "exchange", headers = {}, innerCode, extra = {}, options) {
15
+ super(message, options);
16
+ this.status = status;
17
+ this.code = code;
18
+ this.layer = layer;
19
+ this.headers = headers;
20
+ this.innerCode = innerCode;
21
+ this.extra = extra;
22
+ }
23
+ };
24
+ var TodoErrors = {
25
+ /** Unsupported query option ($select on collections and lists, $orderby, $select on delta). */
26
+ parseUri: () => new GraphError(400, "invalidRequest", "Invalid request", "todo", {}, "RequestBroker--ParseUri"),
27
+ /** Body that does not bind to the model (for example an unknown enum value). */
28
+ invalidModel: () => new GraphError(400, "invalidRequest", "Invalid request", "todo", {}, "InvalidModel"),
29
+ notFound: () => new GraphError(404, "ErrorItemNotFound", "Item not found", "todo", {}, "ErrorItemNotFound"),
30
+ /** Verified live (todo.dates-and-defaults): PATCH displayName of the default list. */
31
+ defaultListRename: () => new GraphError(400, "invalidRequest", "Invalid request", "todo", {}, "ErrorInvalidRequest"),
32
+ /** Verified live (todo.dates-and-defaults): DELETE of the default list. */
33
+ defaultListDelete: () => new GraphError(
34
+ 400,
35
+ "invalidRequest",
36
+ "The default Tasks folder can not be deleted",
37
+ "todo",
38
+ {},
39
+ "CantDeleteDefaultFolder"
40
+ ),
41
+ /** Verified live (todo.flagged-emails): DELETE of the Flagged Emails list. */
42
+ flaggedListDelete: () => new GraphError(
43
+ 400,
44
+ "invalidRequest",
45
+ "The Flagged Emails folder can not be deleted",
46
+ "todo",
47
+ {},
48
+ "InvalidTaskFolderOperation"
49
+ )
50
+ };
51
+ var g = (status, code, message, headers, options) => new GraphError(status, code, message, "graph", headers, void 0, void 0, options);
52
+ var x = (status, code, message, options) => new GraphError(status, code, message, "exchange", void 0, void 0, void 0, options);
53
+ var Errors = {
54
+ /** Verified live (calendar.boundaries). */
55
+ allDayTooShort: () => x(
56
+ 400,
57
+ "ErrorInvalidRequest",
58
+ "Your request can't be completed. The duration of an event marked as All day must be at least 24 hours."
59
+ ),
60
+ /** Verified live (contacts.boundaries). */
61
+ tooManyContactEmails: (n) => x(
62
+ 400,
63
+ "ErrorInvalidProperty",
64
+ `The multi value property emailAddresses has ${n} entries, that exceeds the max allowed value of 3.`
65
+ ),
66
+ /** Verified live (contacts.boundaries): a graph-layer envelope. */
67
+ contactPhotoNotFound: () => g(404, "ErrorItemNotFound", "The specified object was not found in the store."),
68
+ /** Verified live for Start (calendar.allday-order-paging); End assumed to match. */
69
+ allDayNotMidnight: (which) => x(
70
+ 400,
71
+ "ErrorInvalidRequest",
72
+ `Your request can't be completed. The Event.${which} property for an all-day event needs to be set to midnight.`
73
+ ),
74
+ /** Verified live (places.rooms): an unknown place id or address. */
75
+ placeNotFound: (id) => g(404, "notFound", `Place with identity ${id} is not found`),
76
+ /** Verified live (permissions.write-places-and-translate): PATCH with Place.Read.All only. */
77
+ placesWriteForbidden: () => g(
78
+ 403,
79
+ "forbidden",
80
+ "Insufficient privileges to complete the operation. The signed-in user must be assigned a supported Microsoft Entra role. Exchange Administrator is the least privileged role supported for this operation."
81
+ ),
82
+ defaultCalendarDelete: () => x(400, "ErrorInvalidRequest", "Your request can't be completed. The default calendar cannot be deleted."),
83
+ // ---- authentication (front end) ----
84
+ tokenEmpty: () => g(401, "InvalidAuthenticationToken", "Access token is empty.", wwwAuthenticate()),
85
+ tokenUnreadable: (options) => g(
86
+ 401,
87
+ "InvalidAuthenticationToken",
88
+ "Protocol 'Bearer' failed to validate because The token could not be read.",
89
+ wwwAuthenticate(),
90
+ options
91
+ ),
92
+ tokenNoAppId: () => g(401, "InvalidAuthenticationToken", "NoAppIdInToken", wwwAuthenticate()),
93
+ tokenExpired: () => g(401, "InvalidAuthenticationToken", "Lifetime validation failed, the token is expired.", wwwAuthenticate()),
94
+ tokenSignature: () => g(401, "InvalidAuthenticationToken", "Signature validation failed.", wwwAuthenticate()),
95
+ /** Observed on Graph (docs/research/behavior-research.md, InvalidAuthenticationToken). */
96
+ tokenAudience: () => g(401, "InvalidAuthenticationToken", "Access token validation failure. Invalid audience.", wwwAuthenticate()),
97
+ /** A token this emulator signed that was revoked since (the revoke endpoint is an emulator extension). */
98
+ tokenInvalid: () => g(401, "InvalidAuthenticationToken", "Access token validation failure.", wwwAuthenticate()),
99
+ meRequiresDelegated: () => g(400, "BadRequest", "/me request is only valid with delegated authentication flow."),
100
+ directoryDenied: () => g(403, "Authorization_RequestDenied", "Insufficient privileges to complete the operation."),
101
+ directoryMethodNotAllowed: () => g(405, "Request_BadRequest", "Specified HTTP method is not allowed for the request target."),
102
+ // ---- routing / OData (front end) ----
103
+ invalidVersion: (v) => g(404, "ResourceNotFound", `Invalid version: ${v}`),
104
+ directoryResourceNotFound: (segment) => g(
105
+ 404,
106
+ "Request_ResourceNotFound",
107
+ `Resource '${segment}' does not exist or one of its queried reference-property objects are not present.`
108
+ ),
109
+ segmentNotFound: (segment) => g(400, "BadRequest", `Resource not found for the segment '${segment}'.`),
110
+ noContentType: () => g(400, "BadRequest", "Write requests (excluding DELETE) must contain the Content-Type header declaration."),
111
+ unreadableJson: () => g(
112
+ 400,
113
+ "BadRequest",
114
+ "Unable to read JSON request payload. Please ensure Content-Type header is set and payload is of valid JSON format."
115
+ ),
116
+ selectExpandParse: (prop, type) => g(
117
+ 400,
118
+ "BadRequest",
119
+ `Parsing OData Select and Expand failed: Could not find a property named '${prop}' on type 'microsoft.graph.${type}'.`
120
+ ),
121
+ invalidTop: (v) => g(
122
+ 400,
123
+ "BadRequest",
124
+ `Invalid value '${v}' for $top query option found. The $top query option requires a non-negative integer value.`
125
+ ),
126
+ invalidSkip: (v) => g(
127
+ 400,
128
+ "BadRequest",
129
+ `Invalid value '${v}' for $skip query option found. The $skip query option requires a non-negative integer value.`
130
+ ),
131
+ filterClause: (detail) => g(400, "BadRequest", `Invalid filter clause: ${detail}`),
132
+ orderbyClause: (detail) => g(400, "BadRequest", `Invalid orderby clause: ${detail}`),
133
+ badDeltaToken: () => g(400, "BadRequest", "Badly formed token."),
134
+ changeTrackingUnsupported: (type) => g(400, "BadRequest", `Unsupported request: Change tracking is not supported against 'microsoft.graph.${type}'.`),
135
+ batchLimit: () => g(400, "BadRequest", "Number of requests inside batch exceed the limit."),
136
+ batchDuplicateId: (id) => g(400, "BadRequest", `Request Id ${id} has to be unique in a batch.`),
137
+ batchMissingContentType: (id) => g(400, "BadRequest", `Write request with id '${id}' does not contain a Content-Type header.`),
138
+ batchInvalid: (options) => g(400, "BadRequest", "Invalid batch payload format.", void 0, options),
139
+ batchFailedDependency: () => g(424, "FailedDependency", "Request failed as dependent request failed"),
140
+ methodNotAllowed: () => x(405, "ErrorInvalidRequest", "The OData request is not supported."),
141
+ unsupportedRequest: (detail) => g(400, "BadRequest", `Unsupported request: ${detail}`),
142
+ // ---- Outlook REST query layer ----
143
+ parseUriProperty: (prop, type = "microsoft.graph.message", withInner = false) => withInner ? g(400, "RequestBroker--ParseUri", `Could not find a property named '${prop}' on type '${type}'.`) : x(400, "RequestBroker--ParseUri", `Could not find a property named '${prop}' on type '${type}'.`),
144
+ inefficientFilter: () => x(400, "InefficientFilter", "The restriction or sort order is too complex for this operation."),
145
+ invalidFilterNodes: () => x(400, "ErrorInvalidUrlQueryFilter", "The query filter contains one or more invalid nodes."),
146
+ deltaFilterUnsupported: () => x(
147
+ 400,
148
+ "ErrorInvalidUrlQuery",
149
+ "The '$filter' parameter value is not supported for 'Messages' resource; only a value of 'receivedDateTime ge value' is supported."
150
+ ),
151
+ searchWithFilter: () => x(400, "SearchWithFilter", "The query parameter '$filter' is not supported with '$search'."),
152
+ searchWithOrderBy: () => x(400, "SearchWithOrderBy", "The query parameter '$orderBy' is not supported with '$search'."),
153
+ unsupportedSortPath: () => x(400, "ErrorUnsupportedPathForSortGroup", "The property can not be used for sorting or grouping results."),
154
+ searchWithSkip: () => x(400, "SearchWithSkip", "The query parameter '$skip' is not supported with '$search'."),
155
+ // ---- store ----
156
+ idMalformed: () => x(400, "ErrorInvalidIdMalformed", "Id is malformed."),
157
+ itemNotFound: () => x(
158
+ 404,
159
+ "ErrorItemNotFound",
160
+ "The specified object was not found in the store., The process failed to get the correct properties."
161
+ ),
162
+ attachmentNotFound: () => x(404, "ErrorItemNotFound", "The specified object was not found in the store."),
163
+ /**
164
+ * Verified live (delegated.access-and-zones): a delegated caller with
165
+ * .Shared scopes but no access to another user's mailbox gets 404, not 403.
166
+ * Under a well-known folder the message names the folder.
167
+ */
168
+ mailboxNotAccessible: (defaultFolder) => x(
169
+ 404,
170
+ "ErrorItemNotFound",
171
+ defaultFolder ? `The specified object was not found in the store., Default folder ${defaultFolder} not found.` : "The specified object was not found in the store."
172
+ ),
173
+ folderNotFound: () => x(404, "ErrorFolderNotFound", "The specified folder could not be found in the store."),
174
+ wrongMailbox: (id, mailboxGuid) => x(404, "ErrorInvalidMailboxItemId", `Item '${id}' doesn't belong to the targeted mailbox '${mailboxGuid}'.`),
175
+ folderIdForItemId: () => x(400, "ErrorCannotUseFolderIdForItemId", "Expected an item Id but received a folder Id."),
176
+ itemIdForFolderId: () => x(400, "ErrorCannotUseItemIdForFolderId", "Expected a folder Id but received an item Id."),
177
+ invalidUser: (who) => x(404, "ErrorInvalidUser", `The requested user '${who}' is invalid.`),
178
+ mailboxNotEnabled: () => x(404, "MailboxNotEnabledForRESTAPI", "The mailbox is either inactive, soft-deleted, or is hosted on-premise."),
179
+ accessDenied: (options) => x(403, "ErrorAccessDenied", "Access is denied. Check credentials and try again.", options),
180
+ cannotResubmit: () => x(403, "ErrorAccessDenied", "Access is denied. Check credentials and try again., Cannot submit message."),
181
+ sendAsDenied: () => x(
182
+ 403,
183
+ "ErrorSendAsDenied",
184
+ "The user account which was used to submit this request does not have the right to send mail on behalf of the specified sending account., Cannot submit message."
185
+ ),
186
+ applicationAccessPolicy: () => x(
187
+ 403,
188
+ "ErrorAccessDenied",
189
+ "Access to OData is disabled: [RAOP] : Blocked by tenant configured AppOnly AccessPolicy settings."
190
+ ),
191
+ invalidReferenceItem: () => x(400, "ErrorInvalidReferenceItem", "The reference item does not support the requested operation."),
192
+ invalidChangeKey: () => x(400, "ErrorInvalidChangeKey", "The change key is invalid."),
193
+ bodyContentTypeNotSupported: () => x(
194
+ 400,
195
+ "ErrorBodyContentTypeNotSupported",
196
+ "The body content type in the body-content-type preferred header is not supported."
197
+ ),
198
+ attachmentTooSmall: () => x(400, "ErrorAttachmentSizeShouldBeGreaterThanZero", "Attachment size must be greater than the minimum size."),
199
+ attachmentTooLarge: () => x(
200
+ 400,
201
+ "ErrorAttachmentSizeShouldNotBeMoreThanMaximumSize",
202
+ "Attachment size must be greater than the minimum size."
203
+ ),
204
+ requestEntityTooLarge: () => x(413, "ErrorRequestEntityTooLarge", "The request entity is too large."),
205
+ throttled: (retryAfter) => new GraphError(429, "ApplicationThrottled", "Application is over its MailboxConcurrency limit.", "exchange", {
206
+ "Retry-After": String(retryAfter)
207
+ }),
208
+ // ---- folders ----
209
+ folderExists: (name) => x(409, "ErrorFolderExists", `A folder with the specified name already exists., Could not create folder '${name}'.`),
210
+ folderSaveProperty: () => x(400, "ErrorFolderSavePropertyError", "The folder save operation failed due to invalid property values."),
211
+ deleteDistinguished: () => x(400, "ErrorDeleteDistinguishedFolder", "Distinguished folders cannot be deleted."),
212
+ moveCopyFailed: () => x(500, "ErrorMoveCopyFailed", "The move or copy operation failed."),
213
+ // ---- payload ----
214
+ requiredProperty: (prop) => x(400, "ErrorInvalidProperty", `The property '${prop}' is required when creating the entity.`),
215
+ missingParameters: (op, params) => x(
216
+ 400,
217
+ "RequestBodyRead",
218
+ `One or more parameters of the operation '${op}' are missing from the request payload. The missing parameters are: ${params.join(", ")}.`
219
+ ),
220
+ unknownBodyProperty: (prop, type) => x(
221
+ 400,
222
+ "RequestBodyRead",
223
+ `The property '${prop}' does not exist on type 'microsoft.graph.${type}'. Make sure to only use property names that are defined by the type or mark the type as open type.`
224
+ ),
225
+ enumValueNotFound: (value) => x(400, "RequestBodyRead", `Requested value '${value}' was not found.`),
226
+ literalConversion: (literal2, type) => x(400, "RequestBodyRead", `Cannot convert the literal '${literal2}' to the expected type '${type}'.`),
227
+ unableToDeserialize: () => x(400, "UnableToDeserializePostBody", "were unable to deserialize "),
228
+ internalServerError: () => x(500, "ErrorInternalServerError", "An internal server error occurred. The operation failed."),
229
+ internalServerErrorDetail: (detail) => x(500, "ErrorInternalServerError", `An internal server error occurred. The operation failed., ${detail}`),
230
+ invalidSourceFolderIds: () => x(400, "ErrorInvalidSourceFolderIds", "Source Folder Ids cannot be empty."),
231
+ filterQueryParse: (detail) => x(400, "ErrorParsingFilterQuery-ParseUri", detail),
232
+ extensionExpandRequiresFilter: () => x(
233
+ 400,
234
+ "ErrorGraphExtensionExpandRequiresFilter",
235
+ "When expanding extensions, a filter must be provided to specify which extensions to expand. For example $expand=Extensions($filter=Id eq 'Com.Insightly.CRMOpportunity')."
236
+ ),
237
+ abstractType: (type) => x(422, "UnprocessableType", `Cannot process input of abstract type 'microsoft.graph.${type}'`),
238
+ parameterEmpty: (name) => x(400, "ErrorInvalidParameter", `The value of the parameter '${name}' is empty.`),
239
+ // ---- send ----
240
+ noRecipients: () => x(
241
+ 400,
242
+ "ErrorInvalidRecipients",
243
+ "At least one recipient is not valid., A message can't be sent because it contains no recipients."
244
+ ),
245
+ unresolvedRecipient: (addr) => x(
246
+ 400,
247
+ "ErrorInvalidRecipients",
248
+ `At least one recipient is not valid., Recipient '${addr}' is not resolved. All recipients must be resolved before a message can be submitted.`
249
+ ),
250
+ missingRecipients: () => x(400, "ErrorMissingRecipients", "A message needs to have at least one recipient."),
251
+ forwardRecipientsEmpty: () => x(
252
+ 400,
253
+ "ErrorParameterValueEmpty",
254
+ "The value of the parameter 'ToRecipients' is empty. Specify 'ToRecipients' either in the message object or in the action."
255
+ ),
256
+ irresolvableConflict: () => x(
257
+ 412,
258
+ "ErrorIrresolvableConflict",
259
+ "The send or update operation could not be performed because the change key passed in the request does not match the current change key for the item."
260
+ ),
261
+ // ---- MIME ----
262
+ mimeInvalidBase64: () => x(400, "ErrorMimeContentInvalidBase64String", "Invalid base64 string for MIME content."),
263
+ mimeConversionFailed: () => x(400, "ErrorMimeContentConversionFailed", "The MIME content could not be converted."),
264
+ // ---- upload sessions (Outlook host) ----
265
+ uploadInvalidStart: () => x(400, "InvalidStart", "Invalid Start offset for the current fragment. Fragment might already have been uploaded."),
266
+ uploadInvalidRange: () => x(400, "InvalidContentRange", "The Content-Range header is missing or malformed."),
267
+ // ---- calendar (sources in docs/research/calendar-contacts.md; none verified live) ----
268
+ /** Observed in several SDK issues (3.1). */
269
+ timeWindowRequired: () => x(
270
+ 400,
271
+ "ErrorInvalidParameter",
272
+ "This request requires a time window specified by the query string parameters StartDateTime and EndDateTime."
273
+ ),
274
+ /** Observed code (2); message text assumed. */
275
+ timeZoneNotSupported: (name) => x(
276
+ 400,
277
+ "TimeZoneNotSupportedException",
278
+ `A valid TimeZone value must be specified. The following TimeZone value is not supported: '${name}'.`
279
+ ),
280
+ /** EWS code and message (observed through EWS); Graph surface assumed. */
281
+ endBeforeStart: () => x(400, "ErrorCalendarEndDateIsEarlierThanStartDate", "EndDate is earlier than StartDate"),
282
+ /** Code observed (outlook-mcp); message assumed. */
283
+ propertyValidation: () => x(400, "ErrorPropertyValidationFailure", "At least one property failed validation."),
284
+ invalidRecurrenceRange: () => x(400, "ErrorInvalidRecurrenceRange", "The recurrence range is invalid."),
285
+ /** Observed (outlook-mcp issue #86). */
286
+ recurrenceEndBeforeStart: () => x(400, "ErrorInvalidParameter", "StartDateV2 should be earlier or equal to EndDateV2"),
287
+ /** Documented on event-update. */
288
+ occurrenceCrossingBoundary: () => x(400, "ErrorOccurrenceCrossingBoundary", "Modified occurrence is crossing or overlapping adjacent occurrence."),
289
+ /** Documented message on event-cancel; code assumed. */
290
+ organizerOnlyCancel: () => x(400, "ErrorInvalidRequest", "Your request can't be completed. You need to be an organizer to cancel a meeting."),
291
+ /** Verified live for accept; decline and tentativelyAccept assumed to match. */
292
+ organizerCannotRespond: (_action) => x(
293
+ 400,
294
+ "ErrorInvalidRequest",
295
+ "Your request can't be completed. You can't respond to this meeting because you're the meeting organizer."
296
+ ),
297
+ /** Observed in a Q&A thread for contacts; reused for default calendars (assumed). */
298
+ cannotDeleteObject: () => x(400, "ErrorCannotDeleteObject", "Object cannot be deleted."),
299
+ /** Documented that proposals fail with 400; code and message assumed. */
300
+ proposalsNotAllowed: () => x(
301
+ 400,
302
+ "ErrorInvalidRequest",
303
+ "Your request can't be completed. New time proposals are not allowed for this meeting."
304
+ ),
305
+ /** Observed (outlook-mcp CHANGELOG). */
306
+ missingEventStart: () => x(400, "ErrorInvalidRequest", "Missing parameters: Event.Start"),
307
+ /** Documented: findMeetingTimes has no application permission. */
308
+ delegatedOnly: () => g(401, "UnknownError", ""),
309
+ /** Verified live (workspace.permissions.mail-only-app): places and To Do without permission. */
310
+ unknownAuthError: () => g(401, "UnknownError", ""),
311
+ // Assumed codes for calendar sharing validation (not observed).
312
+ invalidDelegateRole: () => x(400, "ErrorInvalidParameter", "Delegate roles can only be granted on the primary calendar."),
313
+ /** Verified live: a delegate role on a secondary calendar. */
314
+ sharingRoleNotSupported: (role, address) => x(
315
+ 400,
316
+ "ErrorInvalidRequest",
317
+ `Your request can't be completed. Sharing with permission level '${role.charAt(0).toUpperCase()}${role.slice(1)}' is not supported for this calendar for user '${address}'.`
318
+ ),
319
+ /** Verified live: PATCH of a calendarPermission without role. */
320
+ permissionLevelRequired: () => x(
321
+ 400,
322
+ "ErrorInvalidRequest",
323
+ "Your request can't be completed. Permission level needs to be specified when sharing calendar."
324
+ ),
325
+ /** Verified live: a sharee listing the owner's calendarPermissions. */
326
+ notSupported: () => x(400, "ErrorNotSupported", "Specified method is not supported."),
327
+ // ---- generic ----
328
+ notImplemented: (what) => g(501, "NotImplemented", `The emulator does not implement ${what}.`),
329
+ internal: (detail) => g(500, "InternalServerError", detail)
330
+ };
331
+ function wwwAuthenticate() {
332
+ return {
333
+ "WWW-Authenticate": 'Bearer realm="", authorization_uri="https://login.microsoftonline.com/common/oauth2/authorize", client_id="00000003-0000-0000-c000-000000000000"'
334
+ };
335
+ }
336
+ function renderError(err, ctx) {
337
+ if (err.layer === "sharepoint") {
338
+ const error2 = {
339
+ code: err.code,
340
+ ...err.innerCode ? { innerError: { code: err.innerCode } } : {},
341
+ message: err.message,
342
+ ...err.extra
343
+ };
344
+ return { status: err.status, body: { error: error2 }, headers: err.headers };
345
+ }
346
+ const error = { code: err.code, message: err.message, ...err.extra };
347
+ if (err.layer === "graph" || err.layer === "todo") {
348
+ const date = (ctx.now ?? /* @__PURE__ */ new Date()).toISOString().slice(0, 19);
349
+ error.innerError = {
350
+ ...err.innerCode ? { code: err.innerCode } : {},
351
+ date,
352
+ "request-id": ctx.requestId,
353
+ "client-request-id": ctx.clientRequestId
354
+ };
355
+ }
356
+ return { status: err.status, body: { error }, headers: err.headers };
357
+ }
358
+
359
+ // src/graph/exchange/calendar.ts
360
+ import { randomBytes as randomBytes3 } from "crypto";
361
+
362
+ // src/graph/exchange/ids.ts
363
+ import { randomBytes } from "crypto";
364
+ var TRAILING_PADDING = /[=]+$/;
365
+ var URL_SAFE_PADDING_COUNT = /^([A-Za-z0-9\-_]*)([012])$/;
366
+ var BASE64_ANY_ALPHABET = /^[A-Za-z0-9\-_+/]+={0,2}$/;
367
+ var GUID_FORMAT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
368
+ function exchangeBase64(buf) {
369
+ return buf.toString("base64").replace(/\//g, "-").replace(/\+/g, "_");
370
+ }
371
+ function fromExchangeBase64(s) {
372
+ if (!BASE64_ANY_ALPHABET.test(s)) {
373
+ return null;
374
+ }
375
+ const std = s.replace(/-/g, "/").replace(/_/g, "+");
376
+ if (std.replace(TRAILING_PADDING, "").length % 4 === 1) {
377
+ return null;
378
+ }
379
+ return Buffer.from(std, "base64");
380
+ }
381
+ function newStoreIdentity(mailboxGuid) {
382
+ return {
383
+ mailboxGuid,
384
+ providerUid: randomBytes(16).toString("hex"),
385
+ databaseGuid: randomBytes(16).toString("hex")
386
+ };
387
+ }
388
+ function gc(counter) {
389
+ const b = Buffer.alloc(6);
390
+ b.writeUIntBE(counter, 0, 6);
391
+ return b;
392
+ }
393
+ function header(store, entryLen) {
394
+ return Buffer.concat([
395
+ Buffer.from([store.standardIds ? 0 : 1, 3, 36, 0]),
396
+ Buffer.from(store.mailboxGuid, "ascii"),
397
+ Buffer.from([0, entryLen, 0])
398
+ ]);
399
+ }
400
+ function flags(store) {
401
+ return store.standardIds ? Buffer.from([0, 0, 0, 0]) : Buffer.from([0, 3]);
402
+ }
403
+ function encodeFolderId(store, folderCounter) {
404
+ const entry = Buffer.concat([
405
+ flags(store),
406
+ Buffer.from(store.providerUid, "hex"),
407
+ Buffer.from([1, 0]),
408
+ Buffer.from(store.databaseGuid, "hex"),
409
+ gc(folderCounter),
410
+ Buffer.from([0, 0])
411
+ ]);
412
+ return exchangeBase64(Buffer.concat([header(store, 46), entry]));
413
+ }
414
+ function encodeItemId(store, folderCounter, itemCounter) {
415
+ const entry = Buffer.concat([
416
+ flags(store),
417
+ Buffer.from(store.providerUid, "hex"),
418
+ Buffer.from([7, 0]),
419
+ Buffer.from(store.databaseGuid, "hex"),
420
+ gc(folderCounter),
421
+ Buffer.from([0, 0]),
422
+ Buffer.from(store.databaseGuid, "hex"),
423
+ gc(itemCounter),
424
+ Buffer.from([0, 0])
425
+ ]);
426
+ return exchangeBase64(Buffer.concat([header(store, 70), entry]));
427
+ }
428
+ function encodeConversationId(mailboxGuid, conversationGuid) {
429
+ return exchangeBase64(
430
+ Buffer.concat([
431
+ Buffer.from([0, 4, 36, 0]),
432
+ Buffer.from(mailboxGuid, "ascii"),
433
+ Buffer.from([0, 16, 0]),
434
+ conversationGuid
435
+ ])
436
+ );
437
+ }
438
+ var IMMUTABLE_PREFIX = Buffer.from("0009002e00000000001d840311aa6611cd9bc800aa002fc45a", "hex");
439
+ function immutableEntryIdBytes(restImmutable) {
440
+ return Buffer.concat([Buffer.alloc(4), IMMUTABLE_PREFIX.subarray(9), restImmutable.subarray(25)]);
441
+ }
442
+ function restImmutableFromEntryId(bin) {
443
+ if (bin.length !== 46 || bin.readUInt32BE(0) !== 0) {
444
+ return null;
445
+ }
446
+ if (!bin.subarray(4, 20).equals(IMMUTABLE_PREFIX.subarray(9)) || bin[20] !== 13) {
447
+ return null;
448
+ }
449
+ return Buffer.concat([IMMUTABLE_PREFIX, bin.subarray(20)]);
450
+ }
451
+ function foreignEntryIdToRest(store, bin) {
452
+ if (bin.length !== 46 && bin.length !== 70 || bin.readUInt32BE(0) !== 0) {
453
+ return null;
454
+ }
455
+ if (bin[20] !== (bin.length === 46 ? 1 : 7)) {
456
+ return null;
457
+ }
458
+ return exchangeBase64(Buffer.concat([header(store, bin.length === 46 ? 46 : 70), flags(store), bin.subarray(4)]));
459
+ }
460
+ function encodeImmutableId(store, immutableCounter) {
461
+ return exchangeBase64(
462
+ Buffer.concat([
463
+ IMMUTABLE_PREFIX,
464
+ Buffer.from([13, 0]),
465
+ Buffer.from(store.databaseGuid, "hex"),
466
+ gc(immutableCounter),
467
+ Buffer.from([0, 0])
468
+ ])
469
+ );
470
+ }
471
+ function dotnetTicks(ms) {
472
+ return (BigInt(ms) + 62135596800000n) * 10000n;
473
+ }
474
+ function fromDotnetTicks(ticks) {
475
+ return Number(ticks / 10000n - 62135596800000n);
476
+ }
477
+ function encodeOccurrenceId(store, folderCounter, masterCounter, dateMs) {
478
+ const master = fromExchangeBase64(encodeItemId(store, folderCounter, masterCounter));
479
+ const ticks = Buffer.alloc(8);
480
+ ticks.writeBigUInt64BE(dotnetTicks(dateMs));
481
+ return exchangeBase64(
482
+ Buffer.concat([
483
+ master.subarray(0, 40),
484
+ Buffer.from([1, 81, 0, 8]),
485
+ ticks,
486
+ master.subarray(40),
487
+ Buffer.from([0, 16])
488
+ ])
489
+ );
490
+ }
491
+ function decodeId(id) {
492
+ const buf = fromExchangeBase64(id);
493
+ if (!buf) {
494
+ return null;
495
+ }
496
+ if (buf.length === 51 && buf.subarray(0, 25).equals(IMMUTABLE_PREFIX) && buf[25] === 13) {
497
+ return {
498
+ kind: "immutable",
499
+ databaseGuid: buf.subarray(27, 43).toString("hex"),
500
+ immutableCounter: buf.readUIntBE(43, 6)
501
+ };
502
+ }
503
+ if (buf.length < 44) {
504
+ return null;
505
+ }
506
+ if (buf[0] !== 1 && buf[0] !== 0 || buf[1] !== 3 || buf[2] !== 36) {
507
+ return null;
508
+ }
509
+ if (buf[40] === 1 && buf[41] === 81 && buf[42] === 0 && buf[43] === 8 && buf.length > 54) {
510
+ const ticks = buf.readBigUInt64BE(44);
511
+ const inner = Buffer.concat([buf.subarray(0, 40), buf.subarray(52, buf.length - 2)]);
512
+ const master = decodeId(exchangeBase64(inner));
513
+ if (master?.kind !== "item") {
514
+ return null;
515
+ }
516
+ return { ...master, kind: "occurrence", dateMs: fromDotnetTicks(ticks) };
517
+ }
518
+ const mailboxGuid = buf.subarray(4, 40).toString("ascii");
519
+ if (!GUID_FORMAT.test(mailboxGuid)) {
520
+ return null;
521
+ }
522
+ const { 41: len } = buf;
523
+ const o = buf[0] === 1 ? 43 + 2 : 43 + 4;
524
+ const extra = o - 45;
525
+ if (buf.length === 87 + extra && len === 46) {
526
+ return {
527
+ kind: "folder",
528
+ mailboxGuid,
529
+ databaseGuid: buf.subarray(o + 18, o + 34).toString("hex"),
530
+ folderCounter: buf.readUIntBE(o + 34, 6)
531
+ };
532
+ }
533
+ if (buf.length === 111 + extra && len === 70) {
534
+ const folderCounter = buf.readUIntBE(o + 34, 6);
535
+ const itemCounter = buf.readUIntBE(o + 34 + 8 + 16, 6);
536
+ return {
537
+ kind: "item",
538
+ mailboxGuid,
539
+ databaseGuid: buf.subarray(o + 18, o + 34).toString("hex"),
540
+ folderCounter,
541
+ itemCounter
542
+ };
543
+ }
544
+ return null;
545
+ }
546
+ function changeKeyClass(kind) {
547
+ if (kind === "folder") {
548
+ return 1;
549
+ }
550
+ if (kind === "eventLong") {
551
+ return 15;
552
+ }
553
+ return kind === "contact" ? 17 : 9;
554
+ }
555
+ function encodeChangeKey(store, changeNumber, kind = "item") {
556
+ if (kind === "event") {
557
+ const s = Buffer.alloc(22);
558
+ Buffer.from(store.databaseGuid, "hex").copy(s, 0);
559
+ s.writeUIntBE(changeNumber, 16, 6);
560
+ return s.toString("base64");
561
+ }
562
+ const cls = changeKeyClass(kind);
563
+ const b = Buffer.alloc(30);
564
+ b.writeUInt32LE(cls, 0);
565
+ b.writeUInt32LE(22, 4);
566
+ Buffer.from(store.databaseGuid, "hex").copy(b, 8);
567
+ b.writeUIntBE(changeNumber, 24, 6);
568
+ return b.toString("base64");
569
+ }
570
+ function newConversationIndex(at, guid) {
571
+ const filetime = BigInt(at.getTime()) * 10000n + 116444736000000000n;
572
+ const indexHeader = Buffer.alloc(6);
573
+ indexHeader[0] = 1;
574
+ const top = filetime >> 16n;
575
+ for (let i = 0; i < 5; i += 1) {
576
+ indexHeader[1 + i] = Number(top >> BigInt(8 * (5 - i)) & 0xffn);
577
+ }
578
+ return Buffer.concat([indexHeader, guid]);
579
+ }
580
+ function childConversationIndex(parent, at, parentAt) {
581
+ const deltaTicks = BigInt(Math.max(0, at.getTime() - parentAt.getTime())) * 10000n;
582
+ const delta = Number(deltaTicks >> 18n & 0x7fffffffn);
583
+ const block = Buffer.alloc(5);
584
+ block.writeUInt32BE(delta >>> 0, 0);
585
+ block[4] = randomBytes(1)[0];
586
+ return Buffer.concat([parent, block]);
587
+ }
588
+ function newInternetMessageId() {
589
+ const server = `EMUP287MB${String(1e3 + randomBytes(2).readUInt16BE(0) % 9e3)}`;
590
+ return `<${server}${randomBytes(12).toString("hex").toUpperCase().slice(0, 21)}@${server}.EMUP287.PROD.OUTLOOK.COM>`;
591
+ }
592
+ function urlSafeBinary(buf) {
593
+ const b64 = buf.toString("base64");
594
+ const pad = b64.length - b64.replace(TRAILING_PADDING, "").length;
595
+ return `${b64.replace(TRAILING_PADDING, "").replace(/\+/g, "-").replace(/\//g, "_")}${pad}`;
596
+ }
597
+ function fromUrlSafeBinary(s) {
598
+ const m = URL_SAFE_PADDING_COUNT.exec(s);
599
+ if (!m) {
600
+ return null;
601
+ }
602
+ return Buffer.from(m[1].replace(/-/g, "+").replace(/_/g, "/") + "=".repeat(Number(m[2])), "base64");
603
+ }
604
+ function entryIdBytes(store, folderCounter, itemCounter) {
605
+ const parts = [
606
+ Buffer.from([0, 0, 0, 0]),
607
+ Buffer.from(store.providerUid, "hex"),
608
+ Buffer.from([itemCounter === null ? 1 : 7, 0]),
609
+ Buffer.from(store.databaseGuid, "hex"),
610
+ gc(folderCounter),
611
+ Buffer.from([0, 0])
612
+ ];
613
+ if (itemCounter !== null) {
614
+ parts.push(Buffer.from(store.databaseGuid, "hex"), gc(itemCounter), Buffer.from([0, 0]));
615
+ }
616
+ return Buffer.concat(parts);
617
+ }
618
+ function parseEntryId(buf, store) {
619
+ if (buf.length !== 46 && buf.length !== 70) {
620
+ return null;
621
+ }
622
+ if (buf.readUInt32BE(0) !== 0) {
623
+ return null;
624
+ }
625
+ if (buf.subarray(4, 20).toString("hex") !== store.providerUid) {
626
+ return null;
627
+ }
628
+ if (buf.subarray(22, 38).toString("hex") !== store.databaseGuid) {
629
+ return null;
630
+ }
631
+ const folderCounter = buf.readUIntBE(38, 6);
632
+ if (buf.length === 46) {
633
+ return buf[20] === 1 ? { folderCounter, itemCounter: null } : null;
634
+ }
635
+ if (buf[20] !== 7) {
636
+ return null;
637
+ }
638
+ return { folderCounter, itemCounter: buf.readUIntBE(62, 6) };
639
+ }
640
+
641
+ // src/graph/exchange/timezones.generated.ts
642
+ var WINDOWS_ZONES = [
643
+ {
644
+ alias: "Dateline Standard Time",
645
+ displayName: "(UTC-12:00) International Date Line West",
646
+ iana: "Etc/GMT+12",
647
+ ianaAll: ["Etc/GMT+12"]
648
+ },
649
+ {
650
+ alias: "UTC-11",
651
+ displayName: "(UTC-11:00) Coordinated Universal Time-11",
652
+ iana: "Etc/GMT+11",
653
+ ianaAll: ["Etc/GMT+11", "Pacific/Pago_Pago", "Pacific/Niue", "Pacific/Midway"]
654
+ },
655
+ {
656
+ alias: "Aleutian Standard Time",
657
+ displayName: "(UTC-10:00) Aleutian Islands",
658
+ iana: "America/Adak",
659
+ ianaAll: ["America/Adak"]
660
+ },
661
+ {
662
+ alias: "Hawaiian Standard Time",
663
+ displayName: "(UTC-10:00) Hawaii",
664
+ iana: "Pacific/Honolulu",
665
+ ianaAll: ["Pacific/Honolulu", "Pacific/Rarotonga", "Pacific/Tahiti", "Etc/GMT+10"]
666
+ },
667
+ {
668
+ alias: "Marquesas Standard Time",
669
+ displayName: "(UTC-09:30) Marquesas Islands",
670
+ iana: "Pacific/Marquesas",
671
+ ianaAll: ["Pacific/Marquesas"]
672
+ },
673
+ {
674
+ alias: "Alaskan Standard Time",
675
+ displayName: "(UTC-09:00) Alaska",
676
+ iana: "America/Anchorage",
677
+ ianaAll: [
678
+ "America/Anchorage",
679
+ "America/Juneau",
680
+ "America/Metlakatla",
681
+ "America/Nome",
682
+ "America/Sitka",
683
+ "America/Yakutat"
684
+ ]
685
+ },
686
+ {
687
+ alias: "UTC-09",
688
+ displayName: "(UTC-09:00) Coordinated Universal Time-09",
689
+ iana: "Etc/GMT+9",
690
+ ianaAll: ["Etc/GMT+9", "Pacific/Gambier"]
691
+ },
692
+ {
693
+ alias: "Pacific Standard Time (Mexico)",
694
+ displayName: "(UTC-08:00) Baja California",
695
+ iana: "America/Tijuana",
696
+ ianaAll: ["America/Tijuana"]
697
+ },
698
+ {
699
+ alias: "British Columbia Standard Time",
700
+ displayName: "(UTC-08:00) British Columbia",
701
+ iana: "America/Vancouver",
702
+ ianaAll: ["America/Vancouver"]
703
+ },
704
+ {
705
+ alias: "UTC-08",
706
+ displayName: "(UTC-08:00) Coordinated Universal Time-08",
707
+ iana: "Etc/GMT+8",
708
+ ianaAll: ["Etc/GMT+8", "Pacific/Pitcairn"]
709
+ },
710
+ {
711
+ alias: "Pacific Standard Time",
712
+ displayName: "(UTC-08:00) Pacific Time (US & Canada)",
713
+ iana: "America/Los_Angeles",
714
+ ianaAll: ["America/Los_Angeles"]
715
+ },
716
+ {
717
+ alias: "Alberta Standard Time",
718
+ displayName: "(UTC-07:00) Alberta",
719
+ iana: "America/Edmonton",
720
+ ianaAll: ["America/Edmonton", "America/Inuvik"]
721
+ },
722
+ {
723
+ alias: "US Mountain Standard Time",
724
+ displayName: "(UTC-07:00) Arizona",
725
+ iana: "America/Phoenix",
726
+ ianaAll: [
727
+ "America/Phoenix",
728
+ "America/Creston",
729
+ "America/Dawson_Creek",
730
+ "America/Fort_Nelson",
731
+ "America/Hermosillo",
732
+ "Etc/GMT+7"
733
+ ]
734
+ },
735
+ {
736
+ alias: "Mountain Standard Time (Mexico)",
737
+ displayName: "(UTC-07:00) La Paz, Mazatlan",
738
+ iana: "America/Mazatlan",
739
+ ianaAll: ["America/Mazatlan"]
740
+ },
741
+ {
742
+ alias: "Mountain Standard Time",
743
+ displayName: "(UTC-07:00) Mountain Time (US & Canada)",
744
+ iana: "America/Denver",
745
+ ianaAll: ["America/Denver", "America/Cambridge_Bay", "America/Ciudad_Juarez", "America/Boise"]
746
+ },
747
+ {
748
+ alias: "Yukon Standard Time",
749
+ displayName: "(UTC-07:00) Yukon",
750
+ iana: "America/Whitehorse",
751
+ ianaAll: ["America/Whitehorse", "America/Dawson"]
752
+ },
753
+ {
754
+ alias: "Central America Standard Time",
755
+ displayName: "(UTC-06:00) Central America",
756
+ iana: "America/Guatemala",
757
+ ianaAll: [
758
+ "America/Guatemala",
759
+ "America/Belize",
760
+ "America/Costa_Rica",
761
+ "Pacific/Galapagos",
762
+ "America/Tegucigalpa",
763
+ "America/Managua",
764
+ "America/El_Salvador",
765
+ "Etc/GMT+6"
766
+ ]
767
+ },
768
+ {
769
+ alias: "Central Standard Time",
770
+ displayName: "(UTC-06:00) Central Time (US & Canada)",
771
+ iana: "America/Chicago",
772
+ ianaAll: [
773
+ "America/Chicago",
774
+ "America/Rankin_Inlet",
775
+ "America/Resolute",
776
+ "America/Matamoros",
777
+ "America/Ojinaga",
778
+ "America/Indiana/Knox",
779
+ "America/Indiana/Tell_City",
780
+ "America/Menominee",
781
+ "America/North_Dakota/Beulah",
782
+ "America/North_Dakota/Center",
783
+ "America/North_Dakota/New_Salem"
784
+ ]
785
+ },
786
+ {
787
+ alias: "Easter Island Standard Time",
788
+ displayName: "(UTC-06:00) Easter Island",
789
+ iana: "Pacific/Easter",
790
+ ianaAll: ["Pacific/Easter"]
791
+ },
792
+ {
793
+ alias: "Central Standard Time (Mexico)",
794
+ displayName: "(UTC-06:00) Guadalajara, Mexico City, Monterrey",
795
+ iana: "America/Mexico_City",
796
+ ianaAll: [
797
+ "America/Mexico_City",
798
+ "America/Bahia_Banderas",
799
+ "America/Merida",
800
+ "America/Monterrey",
801
+ "America/Chihuahua",
802
+ ""
803
+ ]
804
+ },
805
+ {
806
+ alias: "Manitoba Standard Time",
807
+ displayName: "(UTC-06:00) Manitoba",
808
+ iana: "America/Winnipeg",
809
+ ianaAll: ["America/Winnipeg"]
810
+ },
811
+ {
812
+ alias: "Canada Central Standard Time",
813
+ displayName: "(UTC-06:00) Saskatchewan",
814
+ iana: "America/Regina",
815
+ ianaAll: ["America/Regina", "America/Swift_Current"]
816
+ },
817
+ {
818
+ alias: "SA Pacific Standard Time",
819
+ displayName: "(UTC-05:00) Bogota, Lima, Quito, Rio Branco",
820
+ iana: "America/Bogota",
821
+ ianaAll: [
822
+ "America/Bogota",
823
+ "America/Rio_Branco",
824
+ "America/Eirunepe",
825
+ "America/Coral_Harbour",
826
+ "America/Guayaquil",
827
+ "America/Jamaica",
828
+ "America/Cayman",
829
+ "America/Panama",
830
+ "America/Lima",
831
+ "Etc/GMT+5"
832
+ ]
833
+ },
834
+ {
835
+ alias: "Eastern Standard Time (Mexico)",
836
+ displayName: "(UTC-05:00) Chetumal",
837
+ iana: "America/Cancun",
838
+ ianaAll: ["America/Cancun"]
839
+ },
840
+ {
841
+ alias: "Eastern Standard Time",
842
+ displayName: "(UTC-05:00) Eastern Time (US & Canada)",
843
+ iana: "America/New_York",
844
+ ianaAll: [
845
+ "America/New_York",
846
+ "America/Nassau",
847
+ "America/Toronto",
848
+ "America/Iqaluit",
849
+ "America/Detroit",
850
+ "America/Indiana/Petersburg",
851
+ "America/Indiana/Vincennes",
852
+ "America/Indiana/Winamac",
853
+ "America/Kentucky/Monticello",
854
+ "America/Louisville"
855
+ ]
856
+ },
857
+ {
858
+ alias: "Haiti Standard Time",
859
+ displayName: "(UTC-05:00) Haiti",
860
+ iana: "America/Port-au-Prince",
861
+ ianaAll: ["America/Port-au-Prince"]
862
+ },
863
+ {
864
+ alias: "Cuba Standard Time",
865
+ displayName: "(UTC-05:00) Havana",
866
+ iana: "America/Havana",
867
+ ianaAll: ["America/Havana"]
868
+ },
869
+ {
870
+ alias: "US Eastern Standard Time",
871
+ displayName: "(UTC-05:00) Indiana (East)",
872
+ iana: "America/Indianapolis",
873
+ ianaAll: ["America/Indianapolis", "America/Indiana/Marengo", "America/Indiana/Vevay"]
874
+ },
875
+ {
876
+ alias: "Turks And Caicos Standard Time",
877
+ displayName: "(UTC-05:00) Turks and Caicos",
878
+ iana: "America/Grand_Turk",
879
+ ianaAll: ["America/Grand_Turk"]
880
+ },
881
+ {
882
+ alias: "Venezuela Standard Time",
883
+ displayName: "(UTC-04:30) Caracas",
884
+ iana: "America/Caracas",
885
+ ianaAll: ["America/Caracas"]
886
+ },
887
+ {
888
+ alias: "Paraguay Standard Time",
889
+ displayName: "(UTC-04:00) Asuncion",
890
+ iana: "America/Asuncion",
891
+ ianaAll: ["America/Asuncion"]
892
+ },
893
+ {
894
+ alias: "Atlantic Standard Time",
895
+ displayName: "(UTC-04:00) Atlantic Time (Canada)",
896
+ iana: "America/Halifax",
897
+ ianaAll: [
898
+ "America/Halifax",
899
+ "Atlantic/Bermuda",
900
+ "America/Glace_Bay",
901
+ "America/Goose_Bay",
902
+ "America/Moncton",
903
+ "America/Thule"
904
+ ]
905
+ },
906
+ {
907
+ alias: "Central Brazilian Standard Time",
908
+ displayName: "(UTC-04:00) Cuiaba",
909
+ iana: "America/Cuiaba",
910
+ ianaAll: ["America/Cuiaba", "America/Campo_Grande"]
911
+ },
912
+ {
913
+ alias: "SA Western Standard Time",
914
+ displayName: "(UTC-04:00) Georgetown, La Paz, Manaus, San Juan",
915
+ iana: "America/La_Paz",
916
+ ianaAll: [
917
+ "America/La_Paz",
918
+ "America/Antigua",
919
+ "America/Anguilla",
920
+ "America/Aruba",
921
+ "America/Barbados",
922
+ "America/St_Barthelemy",
923
+ "America/Kralendijk",
924
+ "America/Manaus",
925
+ "America/Boa_Vista",
926
+ "America/Porto_Velho",
927
+ "America/Blanc-Sablon",
928
+ "America/Curacao",
929
+ "America/Dominica",
930
+ "America/Santo_Domingo",
931
+ "America/Grenada",
932
+ "America/Guadeloupe",
933
+ "America/Guyana",
934
+ "America/St_Kitts",
935
+ "America/St_Lucia",
936
+ "America/Marigot",
937
+ "America/Martinique",
938
+ "America/Montserrat",
939
+ "America/Puerto_Rico",
940
+ "America/Lower_Princes",
941
+ "America/Port_of_Spain",
942
+ "America/St_Vincent",
943
+ "America/Tortola",
944
+ "America/St_Thomas",
945
+ "Etc/GMT+4"
946
+ ]
947
+ },
948
+ {
949
+ alias: "Newfoundland Standard Time",
950
+ displayName: "(UTC-03:30) Newfoundland",
951
+ iana: "America/St_Johns",
952
+ ianaAll: ["America/St_Johns"]
953
+ },
954
+ {
955
+ alias: "Tocantins Standard Time",
956
+ displayName: "(UTC-03:00) Araguaina",
957
+ iana: "America/Araguaina",
958
+ ianaAll: ["America/Araguaina"]
959
+ },
960
+ {
961
+ alias: "E. South America Standard Time",
962
+ displayName: "(UTC-03:00) Brasilia",
963
+ iana: "America/Sao_Paulo",
964
+ ianaAll: ["America/Sao_Paulo"]
965
+ },
966
+ {
967
+ alias: "SA Eastern Standard Time",
968
+ displayName: "(UTC-03:00) Cayenne, Fortaleza",
969
+ iana: "America/Cayenne",
970
+ ianaAll: [
971
+ "America/Cayenne",
972
+ "Antarctica/Rothera",
973
+ "Antarctica/Palmer",
974
+ "America/Fortaleza",
975
+ "America/Belem",
976
+ "America/Maceio",
977
+ "America/Recife",
978
+ "America/Santarem",
979
+ "Atlantic/Stanley",
980
+ "America/Paramaribo",
981
+ "Etc/GMT+3"
982
+ ]
983
+ },
984
+ {
985
+ alias: "Argentina Standard Time",
986
+ displayName: "(UTC-03:00) City of Buenos Aires",
987
+ iana: "America/Buenos_Aires",
988
+ ianaAll: [
989
+ "America/Buenos_Aires",
990
+ "America/Argentina/La_Rioja",
991
+ "America/Argentina/Rio_Gallegos",
992
+ "America/Argentina/Salta",
993
+ "America/Argentina/San_Juan",
994
+ "America/Argentina/San_Luis",
995
+ "America/Argentina/Tucuman",
996
+ "America/Argentina/Ushuaia",
997
+ "America/Catamarca",
998
+ "America/Cordoba",
999
+ "America/Jujuy",
1000
+ "America/Mendoza"
1001
+ ]
1002
+ },
1003
+ {
1004
+ alias: "Greenland Standard Time",
1005
+ displayName: "(UTC-03:00) Greenland",
1006
+ iana: "America/Godthab",
1007
+ ianaAll: ["America/Godthab"]
1008
+ },
1009
+ {
1010
+ alias: "Montevideo Standard Time",
1011
+ displayName: "(UTC-03:00) Montevideo",
1012
+ iana: "America/Montevideo",
1013
+ ianaAll: ["America/Montevideo"]
1014
+ },
1015
+ {
1016
+ alias: "Magallanes Standard Time",
1017
+ displayName: "(UTC-03:00) Punta Arenas",
1018
+ iana: "America/Punta_Arenas",
1019
+ ianaAll: ["America/Punta_Arenas", "America/Coyhaique"]
1020
+ },
1021
+ {
1022
+ alias: "Saint Pierre Standard Time",
1023
+ displayName: "(UTC-03:00) Saint Pierre and Miquelon",
1024
+ iana: "America/Miquelon",
1025
+ ianaAll: ["America/Miquelon"]
1026
+ },
1027
+ {
1028
+ alias: "Bahia Standard Time",
1029
+ displayName: "(UTC-03:00) Salvador",
1030
+ iana: "America/Bahia",
1031
+ ianaAll: ["America/Bahia"]
1032
+ },
1033
+ {
1034
+ alias: "Pacific SA Standard Time",
1035
+ displayName: "(UTC-03:00) Santiago",
1036
+ iana: "America/Santiago",
1037
+ ianaAll: ["America/Santiago"]
1038
+ },
1039
+ {
1040
+ alias: "UTC-02",
1041
+ displayName: "(UTC-02:00) Coordinated Universal Time-02",
1042
+ iana: "Etc/GMT+2",
1043
+ ianaAll: ["Etc/GMT+2", "America/Noronha", "Atlantic/South_Georgia"]
1044
+ },
1045
+ {
1046
+ alias: "Azores Standard Time",
1047
+ displayName: "(UTC-01:00) Azores",
1048
+ iana: "Atlantic/Azores",
1049
+ ianaAll: ["Atlantic/Azores", "America/Scoresbysund"]
1050
+ },
1051
+ {
1052
+ alias: "Cape Verde Standard Time",
1053
+ displayName: "(UTC-01:00) Cabo Verde Is.",
1054
+ iana: "Atlantic/Cape_Verde",
1055
+ ianaAll: ["Atlantic/Cape_Verde", "Etc/GMT+1"]
1056
+ },
1057
+ {
1058
+ alias: "UTC",
1059
+ displayName: "(UTC) Coordinated Universal Time",
1060
+ iana: "Etc/UTC",
1061
+ ianaAll: ["Etc/UTC", "Etc/GMT"]
1062
+ },
1063
+ {
1064
+ alias: "Morocco Standard Time",
1065
+ displayName: "(UTC+00:00) Casablanca",
1066
+ iana: "Africa/Casablanca",
1067
+ ianaAll: ["Africa/Casablanca", "Africa/El_Aaiun"]
1068
+ },
1069
+ {
1070
+ alias: "GMT Standard Time",
1071
+ displayName: "(UTC+00:00) Dublin, Edinburgh, Lisbon, London",
1072
+ iana: "Europe/London",
1073
+ ianaAll: [
1074
+ "Europe/London",
1075
+ "Atlantic/Canary",
1076
+ "Atlantic/Faeroe",
1077
+ "Europe/Guernsey",
1078
+ "Europe/Dublin",
1079
+ "Europe/Isle_of_Man",
1080
+ "Europe/Jersey",
1081
+ "Europe/Lisbon",
1082
+ "Atlantic/Madeira"
1083
+ ]
1084
+ },
1085
+ {
1086
+ alias: "Greenwich Standard Time",
1087
+ displayName: "(UTC+00:00) Monrovia, Reykjavik",
1088
+ iana: "Atlantic/Reykjavik",
1089
+ ianaAll: [
1090
+ "Atlantic/Reykjavik",
1091
+ "Africa/Ouagadougou",
1092
+ "Africa/Abidjan",
1093
+ "Africa/Accra",
1094
+ "America/Danmarkshavn",
1095
+ "Africa/Banjul",
1096
+ "Africa/Conakry",
1097
+ "Africa/Bissau",
1098
+ "Africa/Monrovia",
1099
+ "Africa/Bamako",
1100
+ "Africa/Nouakchott",
1101
+ "Atlantic/St_Helena",
1102
+ "Africa/Freetown",
1103
+ "Africa/Dakar",
1104
+ "Africa/Lome"
1105
+ ]
1106
+ },
1107
+ {
1108
+ alias: "Sao Tome Standard Time",
1109
+ displayName: "(UTC+00:00) Sao Tome",
1110
+ iana: "Africa/Sao_Tome",
1111
+ ianaAll: ["Africa/Sao_Tome"]
1112
+ },
1113
+ {
1114
+ alias: "W. Europe Standard Time",
1115
+ displayName: "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna",
1116
+ iana: "Europe/Berlin",
1117
+ ianaAll: [
1118
+ "Europe/Berlin",
1119
+ "Europe/Andorra",
1120
+ "Europe/Vienna",
1121
+ "Europe/Zurich",
1122
+ "Europe/Busingen",
1123
+ "Europe/Gibraltar",
1124
+ "Europe/Rome",
1125
+ "Europe/Vaduz",
1126
+ "Europe/Luxembourg",
1127
+ "Europe/Monaco",
1128
+ "Europe/Malta",
1129
+ "Europe/Amsterdam",
1130
+ "Europe/Oslo",
1131
+ "Europe/Stockholm",
1132
+ "Arctic/Longyearbyen",
1133
+ "Europe/San_Marino",
1134
+ "Europe/Vatican"
1135
+ ]
1136
+ },
1137
+ {
1138
+ alias: "Central Europe Standard Time",
1139
+ displayName: "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague",
1140
+ iana: "Europe/Budapest",
1141
+ ianaAll: [
1142
+ "Europe/Budapest",
1143
+ "Europe/Tirane",
1144
+ "Europe/Prague",
1145
+ "Europe/Podgorica",
1146
+ "Europe/Belgrade",
1147
+ "Europe/Ljubljana",
1148
+ "Europe/Bratislava"
1149
+ ]
1150
+ },
1151
+ {
1152
+ alias: "Romance Standard Time",
1153
+ displayName: "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris",
1154
+ iana: "Europe/Paris",
1155
+ ianaAll: ["Europe/Paris", "Europe/Brussels", "Europe/Copenhagen", "Europe/Madrid", "Africa/Ceuta"]
1156
+ },
1157
+ {
1158
+ alias: "Central European Standard Time",
1159
+ displayName: "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb",
1160
+ iana: "Europe/Warsaw",
1161
+ ianaAll: ["Europe/Warsaw", "Europe/Sarajevo", "Europe/Zagreb", "Europe/Skopje"]
1162
+ },
1163
+ {
1164
+ alias: "W. Central Africa Standard Time",
1165
+ displayName: "(UTC+01:00) West Central Africa",
1166
+ iana: "Africa/Lagos",
1167
+ ianaAll: [
1168
+ "Africa/Lagos",
1169
+ "Africa/Luanda",
1170
+ "Africa/Porto-Novo",
1171
+ "Africa/Kinshasa",
1172
+ "Africa/Bangui",
1173
+ "Africa/Brazzaville",
1174
+ "Africa/Douala",
1175
+ "Africa/Algiers",
1176
+ "Africa/Libreville",
1177
+ "Africa/Malabo",
1178
+ "Africa/Niamey",
1179
+ "Africa/Ndjamena",
1180
+ "Africa/Tunis",
1181
+ "Etc/GMT-1"
1182
+ ]
1183
+ },
1184
+ {
1185
+ alias: "Namibia Standard Time",
1186
+ displayName: "(UTC+01:00) Windhoek",
1187
+ iana: "Africa/Windhoek",
1188
+ ianaAll: ["Africa/Windhoek"]
1189
+ },
1190
+ {
1191
+ alias: "Jordan Standard Time",
1192
+ displayName: "(UTC+02:00) Amman",
1193
+ iana: "Asia/Amman",
1194
+ ianaAll: ["Asia/Amman"]
1195
+ },
1196
+ {
1197
+ alias: "GTB Standard Time",
1198
+ displayName: "(UTC+02:00) Athens, Bucharest",
1199
+ iana: "Europe/Bucharest",
1200
+ ianaAll: ["Europe/Bucharest", "Asia/Nicosia", "Asia/Famagusta", "Europe/Athens"]
1201
+ },
1202
+ {
1203
+ alias: "Middle East Standard Time",
1204
+ displayName: "(UTC+02:00) Beirut",
1205
+ iana: "Asia/Beirut",
1206
+ ianaAll: ["Asia/Beirut"]
1207
+ },
1208
+ {
1209
+ alias: "Egypt Standard Time",
1210
+ displayName: "(UTC+02:00) Cairo",
1211
+ iana: "Africa/Cairo",
1212
+ ianaAll: ["Africa/Cairo"]
1213
+ },
1214
+ {
1215
+ alias: "Syria Standard Time",
1216
+ displayName: "(UTC+02:00) Damascus",
1217
+ iana: "Asia/Damascus",
1218
+ ianaAll: ["Asia/Damascus"]
1219
+ },
1220
+ {
1221
+ alias: "E. Europe Standard Time",
1222
+ displayName: "(UTC+02:00) E. Europe",
1223
+ iana: "Europe/Chisinau",
1224
+ ianaAll: ["Europe/Chisinau"]
1225
+ },
1226
+ {
1227
+ alias: "West Bank Standard Time",
1228
+ displayName: "(UTC+02:00) Gaza, Hebron",
1229
+ iana: "Asia/Hebron",
1230
+ ianaAll: ["Asia/Hebron", "Asia/Gaza"]
1231
+ },
1232
+ {
1233
+ alias: "South Africa Standard Time",
1234
+ displayName: "(UTC+02:00) Harare, Pretoria",
1235
+ iana: "Africa/Johannesburg",
1236
+ ianaAll: [
1237
+ "Africa/Johannesburg",
1238
+ "Africa/Bujumbura",
1239
+ "Africa/Gaborone",
1240
+ "Africa/Lubumbashi",
1241
+ "Africa/Maseru",
1242
+ "Africa/Blantyre",
1243
+ "Africa/Maputo",
1244
+ "Africa/Kigali",
1245
+ "Africa/Mbabane",
1246
+ "Africa/Lusaka",
1247
+ "Africa/Harare",
1248
+ "Etc/GMT-2"
1249
+ ]
1250
+ },
1251
+ {
1252
+ alias: "FLE Standard Time",
1253
+ displayName: "(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius",
1254
+ iana: "Europe/Kiev",
1255
+ ianaAll: [
1256
+ "Europe/Kiev",
1257
+ "Europe/Mariehamn",
1258
+ "Europe/Sofia",
1259
+ "Europe/Tallinn",
1260
+ "Europe/Helsinki",
1261
+ "Europe/Vilnius",
1262
+ "Europe/Riga"
1263
+ ]
1264
+ },
1265
+ {
1266
+ alias: "South Sudan Standard Time",
1267
+ displayName: "(UTC+02:00) Juba",
1268
+ iana: "Africa/Juba",
1269
+ ianaAll: ["Africa/Juba"]
1270
+ },
1271
+ {
1272
+ alias: "Kaliningrad Standard Time",
1273
+ displayName: "(UTC+02:00) Kaliningrad",
1274
+ iana: "Europe/Kaliningrad",
1275
+ ianaAll: ["Europe/Kaliningrad"]
1276
+ },
1277
+ {
1278
+ alias: "Sudan Standard Time",
1279
+ displayName: "(UTC+02:00) Khartoum",
1280
+ iana: "Africa/Khartoum",
1281
+ ianaAll: ["Africa/Khartoum"]
1282
+ },
1283
+ {
1284
+ alias: "Israel Standard Time",
1285
+ displayName: "(UTC+02:00) Middle East",
1286
+ iana: "Asia/Jerusalem",
1287
+ ianaAll: ["Asia/Jerusalem"]
1288
+ },
1289
+ {
1290
+ alias: "Libya Standard Time",
1291
+ displayName: "(UTC+02:00) Tripoli",
1292
+ iana: "Africa/Tripoli",
1293
+ ianaAll: ["Africa/Tripoli"]
1294
+ },
1295
+ {
1296
+ alias: "Arabic Standard Time",
1297
+ displayName: "(UTC+03:00) Baghdad",
1298
+ iana: "Asia/Baghdad",
1299
+ ianaAll: ["Asia/Baghdad"]
1300
+ },
1301
+ {
1302
+ alias: "Turkey Standard Time",
1303
+ displayName: "(UTC+03:00) Istanbul",
1304
+ iana: "Europe/Istanbul",
1305
+ ianaAll: ["Europe/Istanbul"]
1306
+ },
1307
+ {
1308
+ alias: "Arab Standard Time",
1309
+ displayName: "(UTC+03:00) Kuwait, Riyadh",
1310
+ iana: "Asia/Riyadh",
1311
+ ianaAll: ["Asia/Riyadh", "Asia/Bahrain", "Asia/Kuwait", "Asia/Qatar", "Asia/Aden"]
1312
+ },
1313
+ {
1314
+ alias: "Belarus Standard Time",
1315
+ displayName: "(UTC+03:00) Minsk",
1316
+ iana: "Europe/Minsk",
1317
+ ianaAll: ["Europe/Minsk"]
1318
+ },
1319
+ {
1320
+ alias: "Russian Standard Time",
1321
+ displayName: "(UTC+03:00) Moscow, St. Petersburg, Volgograd (RTZ 2)",
1322
+ iana: "Europe/Moscow",
1323
+ ianaAll: ["Europe/Moscow", "Europe/Kirov", "Europe/Simferopol"]
1324
+ },
1325
+ {
1326
+ alias: "E. Africa Standard Time",
1327
+ displayName: "(UTC+03:00) Nairobi",
1328
+ iana: "Africa/Nairobi",
1329
+ ianaAll: [
1330
+ "Africa/Nairobi",
1331
+ "Antarctica/Syowa",
1332
+ "Africa/Djibouti",
1333
+ "Africa/Asmera",
1334
+ "Africa/Addis_Ababa",
1335
+ "Indian/Comoro",
1336
+ "Indian/Antananarivo",
1337
+ "Africa/Mogadishu",
1338
+ "Africa/Dar_es_Salaam",
1339
+ "Africa/Kampala",
1340
+ "Indian/Mayotte",
1341
+ "Etc/GMT-3"
1342
+ ]
1343
+ },
1344
+ {
1345
+ alias: "Volgograd Standard Time",
1346
+ displayName: "(UTC+03:00) Volgograd",
1347
+ iana: "Europe/Volgograd",
1348
+ ianaAll: ["Europe/Volgograd"]
1349
+ },
1350
+ {
1351
+ alias: "Iran Standard Time",
1352
+ displayName: "(UTC+03:30) Tehran",
1353
+ iana: "Asia/Tehran",
1354
+ ianaAll: ["Asia/Tehran"]
1355
+ },
1356
+ {
1357
+ alias: "Arabian Standard Time",
1358
+ displayName: "(UTC+04:00) Abu Dhabi, Muscat",
1359
+ iana: "Asia/Dubai",
1360
+ ianaAll: ["Asia/Dubai", "Asia/Muscat", "Etc/GMT-4"]
1361
+ },
1362
+ {
1363
+ alias: "Astrakhan Standard Time",
1364
+ displayName: "(UTC+04:00) Astrakhan, Ulyanovsk",
1365
+ iana: "Europe/Astrakhan",
1366
+ ianaAll: ["Europe/Astrakhan", "Europe/Ulyanovsk"]
1367
+ },
1368
+ {
1369
+ alias: "Azerbaijan Standard Time",
1370
+ displayName: "(UTC+04:00) Baku",
1371
+ iana: "Asia/Baku",
1372
+ ianaAll: ["Asia/Baku"]
1373
+ },
1374
+ {
1375
+ alias: "Russia Time Zone 3",
1376
+ displayName: "(UTC+04:00) Izhevsk, Samara",
1377
+ iana: "Europe/Samara",
1378
+ ianaAll: ["Europe/Samara"]
1379
+ },
1380
+ {
1381
+ alias: "Mauritius Standard Time",
1382
+ displayName: "(UTC+04:00) Port Louis",
1383
+ iana: "Indian/Mauritius",
1384
+ ianaAll: ["Indian/Mauritius", "Indian/Reunion", "Indian/Mahe"]
1385
+ },
1386
+ {
1387
+ alias: "Saratov Standard Time",
1388
+ displayName: "(UTC+04:00) Saratov",
1389
+ iana: "Europe/Saratov",
1390
+ ianaAll: ["Europe/Saratov"]
1391
+ },
1392
+ {
1393
+ alias: "Georgian Standard Time",
1394
+ displayName: "(UTC+04:00) Tbilisi",
1395
+ iana: "Asia/Tbilisi",
1396
+ ianaAll: ["Asia/Tbilisi"]
1397
+ },
1398
+ {
1399
+ alias: "Caucasus Standard Time",
1400
+ displayName: "(UTC+04:00) Yerevan",
1401
+ iana: "Asia/Yerevan",
1402
+ ianaAll: ["Asia/Yerevan"]
1403
+ },
1404
+ {
1405
+ alias: "Afghanistan Standard Time",
1406
+ displayName: "(UTC+04:30) Kabul",
1407
+ iana: "Asia/Kabul",
1408
+ ianaAll: ["Asia/Kabul"]
1409
+ },
1410
+ {
1411
+ alias: "West Asia Standard Time",
1412
+ displayName: "(UTC+05:00) Ashgabat, Tashkent",
1413
+ iana: "Asia/Tashkent",
1414
+ ianaAll: [
1415
+ "Asia/Tashkent",
1416
+ "Antarctica/Mawson",
1417
+ "Asia/Oral",
1418
+ "Asia/Almaty",
1419
+ "Asia/Aqtau",
1420
+ "Asia/Aqtobe",
1421
+ "Asia/Atyrau",
1422
+ "Asia/Qostanay",
1423
+ "Indian/Maldives",
1424
+ "Indian/Kerguelen",
1425
+ "Asia/Dushanbe",
1426
+ "Asia/Ashgabat",
1427
+ "Asia/Samarkand",
1428
+ "Etc/GMT-5"
1429
+ ]
1430
+ },
1431
+ {
1432
+ alias: "Ekaterinburg Standard Time",
1433
+ displayName: "(UTC+05:00) Ekaterinburg",
1434
+ iana: "Asia/Yekaterinburg",
1435
+ ianaAll: ["Asia/Yekaterinburg"]
1436
+ },
1437
+ {
1438
+ alias: "Pakistan Standard Time",
1439
+ displayName: "(UTC+05:00) Islamabad, Karachi",
1440
+ iana: "Asia/Karachi",
1441
+ ianaAll: ["Asia/Karachi"]
1442
+ },
1443
+ {
1444
+ alias: "Qyzylorda Standard Time",
1445
+ displayName: "(UTC+05:00) Qyzylorda",
1446
+ iana: "Asia/Qyzylorda",
1447
+ ianaAll: ["Asia/Qyzylorda"]
1448
+ },
1449
+ {
1450
+ alias: "India Standard Time",
1451
+ displayName: "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi",
1452
+ iana: "Asia/Calcutta",
1453
+ ianaAll: ["Asia/Calcutta"]
1454
+ },
1455
+ {
1456
+ alias: "Sri Lanka Standard Time",
1457
+ displayName: "(UTC+05:30) Sri Jayawardenepura",
1458
+ iana: "Asia/Colombo",
1459
+ ianaAll: ["Asia/Colombo"]
1460
+ },
1461
+ {
1462
+ alias: "Nepal Standard Time",
1463
+ displayName: "(UTC+05:45) Kathmandu",
1464
+ iana: "Asia/Katmandu",
1465
+ ianaAll: ["Asia/Katmandu"]
1466
+ },
1467
+ {
1468
+ alias: "Central Asia Standard Time",
1469
+ displayName: "(UTC+06:00) Astana",
1470
+ iana: "Asia/Bishkek",
1471
+ ianaAll: ["Asia/Bishkek", "Antarctica/Vostok", "Asia/Urumqi", "Indian/Chagos", "Etc/GMT-6"]
1472
+ },
1473
+ {
1474
+ alias: "Bangladesh Standard Time",
1475
+ displayName: "(UTC+06:00) Dhaka",
1476
+ iana: "Asia/Dhaka",
1477
+ ianaAll: ["Asia/Dhaka", "Asia/Thimphu"]
1478
+ },
1479
+ {
1480
+ alias: "Omsk Standard Time",
1481
+ displayName: "(UTC+06:00) Omsk",
1482
+ iana: "Asia/Omsk",
1483
+ ianaAll: ["Asia/Omsk"]
1484
+ },
1485
+ {
1486
+ alias: "Myanmar Standard Time",
1487
+ displayName: "(UTC+06:30) Yangon (Rangoon)",
1488
+ iana: "Asia/Rangoon",
1489
+ ianaAll: ["Asia/Rangoon", "Indian/Cocos"]
1490
+ },
1491
+ {
1492
+ alias: "SE Asia Standard Time",
1493
+ displayName: "(UTC+07:00) Bangkok, Hanoi, Jakarta",
1494
+ iana: "Asia/Bangkok",
1495
+ ianaAll: [
1496
+ "Asia/Bangkok",
1497
+ "Antarctica/Davis",
1498
+ "Indian/Christmas",
1499
+ "Asia/Jakarta",
1500
+ "Asia/Pontianak",
1501
+ "Asia/Phnom_Penh",
1502
+ "Asia/Vientiane",
1503
+ "Asia/Saigon",
1504
+ "Etc/GMT-7"
1505
+ ]
1506
+ },
1507
+ {
1508
+ alias: "Altai Standard Time",
1509
+ displayName: "(UTC+07:00) Barnaul, Gorno-Altaysk",
1510
+ iana: "Asia/Barnaul",
1511
+ ianaAll: ["Asia/Barnaul"]
1512
+ },
1513
+ {
1514
+ alias: "W. Mongolia Standard Time",
1515
+ displayName: "(UTC+07:00) Hovd",
1516
+ iana: "Asia/Hovd",
1517
+ ianaAll: ["Asia/Hovd"]
1518
+ },
1519
+ {
1520
+ alias: "North Asia Standard Time",
1521
+ displayName: "(UTC+07:00) Krasnoyarsk",
1522
+ iana: "Asia/Krasnoyarsk",
1523
+ ianaAll: ["Asia/Krasnoyarsk", "Asia/Novokuznetsk"]
1524
+ },
1525
+ {
1526
+ alias: "N. Central Asia Standard Time",
1527
+ displayName: "(UTC+07:00) Novosibirsk",
1528
+ iana: "Asia/Novosibirsk",
1529
+ ianaAll: ["Asia/Novosibirsk"]
1530
+ },
1531
+ {
1532
+ alias: "Tomsk Standard Time",
1533
+ displayName: "(UTC+07:00) Tomsk",
1534
+ iana: "Asia/Tomsk",
1535
+ ianaAll: ["Asia/Tomsk"]
1536
+ },
1537
+ {
1538
+ alias: "China Standard Time",
1539
+ displayName: "(UTC+08:00) Beijing, Chongqing, Hong Kong SAR, Urumqi",
1540
+ iana: "Asia/Shanghai",
1541
+ ianaAll: ["Asia/Shanghai", "Asia/Hong_Kong", "Asia/Macau"]
1542
+ },
1543
+ {
1544
+ alias: "North Asia East Standard Time",
1545
+ displayName: "(UTC+08:00) Irkutsk",
1546
+ iana: "Asia/Irkutsk",
1547
+ ianaAll: ["Asia/Irkutsk"]
1548
+ },
1549
+ {
1550
+ alias: "Singapore Standard Time",
1551
+ displayName: "(UTC+08:00) Kuala Lumpur, Singapore",
1552
+ iana: "Asia/Singapore",
1553
+ ianaAll: [
1554
+ "Asia/Singapore",
1555
+ "Asia/Brunei",
1556
+ "Asia/Makassar",
1557
+ "Asia/Kuala_Lumpur",
1558
+ "Asia/Kuching",
1559
+ "Asia/Manila",
1560
+ "Etc/GMT-8"
1561
+ ]
1562
+ },
1563
+ {
1564
+ alias: "W. Australia Standard Time",
1565
+ displayName: "(UTC+08:00) Perth",
1566
+ iana: "Australia/Perth",
1567
+ ianaAll: ["Australia/Perth"]
1568
+ },
1569
+ {
1570
+ alias: "Taipei Standard Time",
1571
+ displayName: "(UTC+08:00) Taipei",
1572
+ iana: "Asia/Taipei",
1573
+ ianaAll: ["Asia/Taipei"]
1574
+ },
1575
+ {
1576
+ alias: "Ulaanbaatar Standard Time",
1577
+ displayName: "(UTC+08:00) Ulaanbaatar",
1578
+ iana: "Asia/Ulaanbaatar",
1579
+ ianaAll: ["Asia/Ulaanbaatar"]
1580
+ },
1581
+ {
1582
+ alias: "Aus Central W. Standard Time",
1583
+ displayName: "(UTC+08:45) Eucla",
1584
+ iana: "Australia/Eucla",
1585
+ ianaAll: ["Australia/Eucla"]
1586
+ },
1587
+ {
1588
+ alias: "Transbaikal Standard Time",
1589
+ displayName: "(UTC+09:00) Chita",
1590
+ iana: "Asia/Chita",
1591
+ ianaAll: ["Asia/Chita"]
1592
+ },
1593
+ {
1594
+ alias: "Tokyo Standard Time",
1595
+ displayName: "(UTC+09:00) Osaka, Sapporo, Tokyo",
1596
+ iana: "Asia/Tokyo",
1597
+ ianaAll: ["Asia/Tokyo", "Asia/Jayapura", "Pacific/Palau", "Asia/Dili", "Etc/GMT-9"]
1598
+ },
1599
+ {
1600
+ alias: "North Korea Standard Time",
1601
+ displayName: "(UTC+09:00) Pyongyang",
1602
+ iana: "Asia/Pyongyang",
1603
+ ianaAll: ["Asia/Pyongyang"]
1604
+ },
1605
+ {
1606
+ alias: "Korea Standard Time",
1607
+ displayName: "(UTC+09:00) Seoul",
1608
+ iana: "Asia/Seoul",
1609
+ ianaAll: ["Asia/Seoul"]
1610
+ },
1611
+ {
1612
+ alias: "Yakutsk Standard Time",
1613
+ displayName: "(UTC+09:00) Yakutsk",
1614
+ iana: "Asia/Yakutsk",
1615
+ ianaAll: ["Asia/Yakutsk", "Asia/Khandyga"]
1616
+ },
1617
+ {
1618
+ alias: "Cen. Australia Standard Time",
1619
+ displayName: "(UTC+09:30) Adelaide",
1620
+ iana: "Australia/Adelaide",
1621
+ ianaAll: ["Australia/Adelaide", "Australia/Broken_Hill"]
1622
+ },
1623
+ {
1624
+ alias: "AUS Central Standard Time",
1625
+ displayName: "(UTC+09:30) Darwin",
1626
+ iana: "Australia/Darwin",
1627
+ ianaAll: ["Australia/Darwin"]
1628
+ },
1629
+ {
1630
+ alias: "E. Australia Standard Time",
1631
+ displayName: "(UTC+10:00) Brisbane",
1632
+ iana: "Australia/Brisbane",
1633
+ ianaAll: ["Australia/Brisbane", "Australia/Lindeman"]
1634
+ },
1635
+ {
1636
+ alias: "AUS Eastern Standard Time",
1637
+ displayName: "(UTC+10:00) Canberra, Melbourne, Sydney",
1638
+ iana: "Australia/Sydney",
1639
+ ianaAll: ["Australia/Sydney", "Australia/Melbourne"]
1640
+ },
1641
+ {
1642
+ alias: "West Pacific Standard Time",
1643
+ displayName: "(UTC+10:00) Guam, Port Moresby",
1644
+ iana: "Pacific/Port_Moresby",
1645
+ ianaAll: [
1646
+ "Pacific/Port_Moresby",
1647
+ "Antarctica/DumontDUrville",
1648
+ "Pacific/Truk",
1649
+ "Pacific/Guam",
1650
+ "Pacific/Saipan",
1651
+ "Etc/GMT-10"
1652
+ ]
1653
+ },
1654
+ {
1655
+ alias: "Tasmania Standard Time",
1656
+ displayName: "(UTC+10:00) Hobart",
1657
+ iana: "Australia/Hobart",
1658
+ ianaAll: ["Australia/Hobart", "Antarctica/Macquarie"]
1659
+ },
1660
+ {
1661
+ alias: "Vladivostok Standard Time",
1662
+ displayName: "(UTC+10:00) Vladivostok",
1663
+ iana: "Asia/Vladivostok",
1664
+ ianaAll: ["Asia/Vladivostok", "Asia/Ust-Nera"]
1665
+ },
1666
+ {
1667
+ alias: "Lord Howe Standard Time",
1668
+ displayName: "(UTC+10:30) Lord Howe Island",
1669
+ iana: "Australia/Lord_Howe",
1670
+ ianaAll: ["Australia/Lord_Howe"]
1671
+ },
1672
+ {
1673
+ alias: "Bougainville Standard Time",
1674
+ displayName: "(UTC+11:00) Bougainville Island",
1675
+ iana: "Pacific/Bougainville",
1676
+ ianaAll: ["Pacific/Bougainville"]
1677
+ },
1678
+ {
1679
+ alias: "Russia Time Zone 10",
1680
+ displayName: "(UTC+11:00) Chokurdakh",
1681
+ iana: "Asia/Srednekolymsk",
1682
+ ianaAll: ["Asia/Srednekolymsk"]
1683
+ },
1684
+ {
1685
+ alias: "Magadan Standard Time",
1686
+ displayName: "(UTC+11:00) Magadan",
1687
+ iana: "Asia/Magadan",
1688
+ ianaAll: ["Asia/Magadan"]
1689
+ },
1690
+ {
1691
+ alias: "Norfolk Standard Time",
1692
+ displayName: "(UTC+11:00) Norfolk Island",
1693
+ iana: "Pacific/Norfolk",
1694
+ ianaAll: ["Pacific/Norfolk"]
1695
+ },
1696
+ {
1697
+ alias: "Sakhalin Standard Time",
1698
+ displayName: "(UTC+11:00) Sakhalin",
1699
+ iana: "Asia/Sakhalin",
1700
+ ianaAll: ["Asia/Sakhalin"]
1701
+ },
1702
+ {
1703
+ alias: "Central Pacific Standard Time",
1704
+ displayName: "(UTC+11:00) Solomon Is., New Caledonia",
1705
+ iana: "Pacific/Guadalcanal",
1706
+ ianaAll: [
1707
+ "Pacific/Guadalcanal",
1708
+ "Antarctica/Casey",
1709
+ "Pacific/Ponape",
1710
+ "Pacific/Kosrae",
1711
+ "Pacific/Noumea",
1712
+ "Pacific/Efate",
1713
+ "Etc/GMT-11"
1714
+ ]
1715
+ },
1716
+ {
1717
+ alias: "Russia Time Zone 11",
1718
+ displayName: "(UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky",
1719
+ iana: "Asia/Kamchatka",
1720
+ ianaAll: ["Asia/Kamchatka", "Asia/Anadyr"]
1721
+ },
1722
+ {
1723
+ alias: "New Zealand Standard Time",
1724
+ displayName: "(UTC+12:00) Auckland, Wellington",
1725
+ iana: "Pacific/Auckland",
1726
+ ianaAll: ["Pacific/Auckland", "Antarctica/McMurdo"]
1727
+ },
1728
+ {
1729
+ alias: "UTC+12",
1730
+ displayName: "(UTC+12:00) Coordinated Universal Time+12",
1731
+ iana: "Etc/GMT-12",
1732
+ ianaAll: [
1733
+ "Etc/GMT-12",
1734
+ "Pacific/Tarawa",
1735
+ "Pacific/Majuro",
1736
+ "Pacific/Kwajalein",
1737
+ "Pacific/Nauru",
1738
+ "Pacific/Funafuti",
1739
+ "Pacific/Wake",
1740
+ "Pacific/Wallis"
1741
+ ]
1742
+ },
1743
+ {
1744
+ alias: "Fiji Standard Time",
1745
+ displayName: "(UTC+12:00) Fiji",
1746
+ iana: "Pacific/Fiji",
1747
+ ianaAll: ["Pacific/Fiji"]
1748
+ },
1749
+ {
1750
+ alias: "Chatham Islands Standard Time",
1751
+ displayName: "(UTC+12:45) Chatham Islands",
1752
+ iana: "Pacific/Chatham",
1753
+ ianaAll: ["Pacific/Chatham"]
1754
+ },
1755
+ {
1756
+ alias: "UTC+13",
1757
+ displayName: "(UTC+13:00) Coordinated Universal Time+13",
1758
+ iana: "Etc/GMT-13",
1759
+ ianaAll: ["Etc/GMT-13", "Pacific/Enderbury", "Pacific/Fakaofo"]
1760
+ },
1761
+ {
1762
+ alias: "Tonga Standard Time",
1763
+ displayName: "(UTC+13:00) Nuku'alofa",
1764
+ iana: "Pacific/Tongatapu",
1765
+ ianaAll: ["Pacific/Tongatapu"]
1766
+ },
1767
+ {
1768
+ alias: "Samoa Standard Time",
1769
+ displayName: "(UTC+13:00) Samoa",
1770
+ iana: "Pacific/Apia",
1771
+ ianaAll: ["Pacific/Apia"]
1772
+ },
1773
+ {
1774
+ alias: "Line Islands Standard Time",
1775
+ displayName: "(UTC+14:00) Kiritimati Island",
1776
+ iana: "Pacific/Kiritimati",
1777
+ ianaAll: ["Pacific/Kiritimati", "Etc/GMT-14"]
1778
+ }
1779
+ ];
1780
+
1781
+ // src/graph/exchange/timezones.ts
1782
+ var TIMEZONE_ABBREVIATION = /^[A-Za-z]{2,5}$/;
1783
+ var byWindows = new Map(WINDOWS_ZONES.map((z) => [z.alias.toLowerCase(), z]));
1784
+ var byIana = /* @__PURE__ */ new Map();
1785
+ for (const z of WINDOWS_ZONES) {
1786
+ for (const i of z.ianaAll) {
1787
+ if (!byIana.has(i.toLowerCase())) {
1788
+ byIana.set(i.toLowerCase(), z);
1789
+ }
1790
+ }
1791
+ }
1792
+ function validIana(name) {
1793
+ try {
1794
+ return new Intl.DateTimeFormat("en-US", { timeZone: name }).resolvedOptions().timeZone;
1795
+ } catch {
1796
+ return null;
1797
+ }
1798
+ }
1799
+ function resolveZone(name) {
1800
+ if (!name) {
1801
+ return null;
1802
+ }
1803
+ const trimmed = name.trim();
1804
+ const lower = trimmed.toLowerCase();
1805
+ if (lower === "utc" || lower === "etc/utc" || lower === "coordinated universal time") {
1806
+ return { iana: "UTC", windows: byWindows.get("utc") ?? null };
1807
+ }
1808
+ const w = byWindows.get(lower);
1809
+ if (w) {
1810
+ return { iana: w.iana, windows: w };
1811
+ }
1812
+ if (TIMEZONE_ABBREVIATION.test(trimmed) && lower !== "gmt") {
1813
+ return null;
1814
+ }
1815
+ const iana = validIana(trimmed);
1816
+ if (iana) {
1817
+ return { iana, windows: byIana.get(iana.toLowerCase()) ?? byIana.get(lower) ?? null };
1818
+ }
1819
+ return null;
1820
+ }
1821
+ function offsetAt(iana, at) {
1822
+ if (iana === "UTC") {
1823
+ return 0;
1824
+ }
1825
+ const parts = new Intl.DateTimeFormat("en-US", {
1826
+ timeZone: iana,
1827
+ hourCycle: "h23",
1828
+ year: "numeric",
1829
+ month: "2-digit",
1830
+ day: "2-digit",
1831
+ hour: "2-digit",
1832
+ minute: "2-digit",
1833
+ second: "2-digit"
1834
+ }).formatToParts(at);
1835
+ const get = (t) => Number(parts.find((p) => p.type === t)?.value ?? 0);
1836
+ const asUtc = Date.UTC(get("year"), get("month") - 1, get("day"), get("hour"), get("minute"), get("second"));
1837
+ return Math.round((asUtc - Math.floor(at.getTime() / 1e3) * 1e3) / 6e4);
1838
+ }
1839
+ var LOCAL_RE = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,7}))?)?)?(Z|[+-]\d{2}:\d{2})?$/;
1840
+ function parseLocal(s) {
1841
+ const m = LOCAL_RE.exec(s.trim());
1842
+ if (!m) {
1843
+ return null;
1844
+ }
1845
+ const [, y, mo, d, h = "0", mi = "0", se = "0", frac = "0", off] = m;
1846
+ const ms = Date.UTC(Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi), Number(se)) + Math.floor(Number(`0.${frac}`) * 1e3);
1847
+ if (Number(mo) < 1 || Number(mo) > 12 || Number(d) < 1 || Number(d) > 31 || Number(h) > 23 || Number(mi) > 59) {
1848
+ return null;
1849
+ }
1850
+ let offsetMinutes = null;
1851
+ if (off === "Z") {
1852
+ offsetMinutes = 0;
1853
+ } else if (off) {
1854
+ offsetMinutes = (off[0] === "-" ? -1 : 1) * (Number(off.slice(1, 3)) * 60 + Number(off.slice(4, 6)));
1855
+ }
1856
+ return { ms, offsetMinutes };
1857
+ }
1858
+ function wallToUtc(wallMs, iana) {
1859
+ if (iana === "UTC") {
1860
+ return wallMs;
1861
+ }
1862
+ let guess = wallMs - offsetAt(iana, new Date(wallMs)) * 6e4;
1863
+ for (let i = 0; i < 3; i += 1) {
1864
+ const next = wallMs - offsetAt(iana, new Date(guess)) * 6e4;
1865
+ if (next === guess) {
1866
+ break;
1867
+ }
1868
+ guess = next;
1869
+ }
1870
+ return guess;
1871
+ }
1872
+ function utcToWall(utcMs, iana) {
1873
+ return utcMs + offsetAt(iana, new Date(utcMs)) * 6e4;
1874
+ }
1875
+ function formatGraphDateTime(wallMs) {
1876
+ const iso = new Date(wallMs).toISOString();
1877
+ return `${iso.slice(0, 19)}.${iso.slice(20, 23)}0000`;
1878
+ }
1879
+ function displayZoneName(input, zone) {
1880
+ if (zone.windows && zone.windows.alias.toLowerCase() === input.trim().toLowerCase()) {
1881
+ return zone.windows.alias;
1882
+ }
1883
+ if (input.trim().toLowerCase() === "utc") {
1884
+ return "UTC";
1885
+ }
1886
+ return zone.iana;
1887
+ }
1888
+
1889
+ // src/graph/exchange/exchange.ts
1890
+ import { randomBytes as randomBytes2, randomUUID } from "crypto";
1891
+ var MILLISECONDS_SUFFIX = /\.\d{3}Z$/;
1892
+ var STATE_KEY = "microsoft.exchange";
1893
+ var DEFAULT_TREE = {
1894
+ name: "Root",
1895
+ isHidden: true,
1896
+ children: [
1897
+ {
1898
+ name: "Top of Information Store",
1899
+ wellKnownName: "msgfolderroot",
1900
+ children: [
1901
+ { name: "Archive", wellKnownName: "archive" },
1902
+ { name: "Calendar", wellKnownName: "calendar", folderClass: "IPF.Appointment" },
1903
+ { name: "Clutter", wellKnownName: "clutter" },
1904
+ { name: "Contacts", wellKnownName: "contacts", folderClass: "IPF.Contact" },
1905
+ { name: "Conversation Action Settings", folderClass: "IPF.Configuration", isHidden: true },
1906
+ {
1907
+ name: "Conversation History",
1908
+ wellKnownName: "conversationhistory",
1909
+ children: [{ name: "Team Chat", folderClass: "IPF.SkypeTeams.Message" }]
1910
+ },
1911
+ { name: "Deleted Items", wellKnownName: "deleteditems" },
1912
+ { name: "Drafts", wellKnownName: "drafts" },
1913
+ { name: "ExternalContacts", folderClass: "IPF.Contact", isHidden: true },
1914
+ { name: "Files", folderClass: "IPF.Files", isHidden: true },
1915
+ { name: "Inbox", wellKnownName: "inbox", baseSize: 1484 },
1916
+ { name: "Journal", folderClass: "IPF.Journal" },
1917
+ { name: "Junk Email", wellKnownName: "junkemail" },
1918
+ { name: "Notes", folderClass: "IPF.StickyNote" },
1919
+ { name: "Outbox", wellKnownName: "outbox" },
1920
+ { name: "Sent Items", wellKnownName: "sentitems" },
1921
+ { name: "Snoozed", wellKnownName: "scheduled" },
1922
+ { name: "Tasks", wellKnownName: "tasks", folderClass: "IPF.Task" }
1923
+ ]
1924
+ },
1925
+ { name: "Finder", wellKnownName: "searchfolders", folderClass: "IPF" },
1926
+ {
1927
+ name: "Recoverable Items",
1928
+ wellKnownName: "recoverableitemsroot",
1929
+ isHidden: true,
1930
+ children: [
1931
+ { name: "Deletions", wellKnownName: "recoverableitemsdeletions" },
1932
+ { name: "Purges", wellKnownName: "recoverableitemspurges" },
1933
+ { name: "Versions", wellKnownName: "recoverableitemsversions" }
1934
+ ]
1935
+ }
1936
+ ]
1937
+ };
1938
+ var ABSENT_WELL_KNOWN = /* @__PURE__ */ new Set(["syncissues", "conflicts", "localfailures", "serverfailures"]);
1939
+ var MAIL_WELL_KNOWN = /* @__PURE__ */ new Set([
1940
+ "archive",
1941
+ "clutter",
1942
+ "conversationhistory",
1943
+ "deleteditems",
1944
+ "drafts",
1945
+ "inbox",
1946
+ "junkemail",
1947
+ "msgfolderroot",
1948
+ "outbox",
1949
+ "recoverableitemsdeletions",
1950
+ "scheduled",
1951
+ "searchfolders",
1952
+ "sentitems",
1953
+ "recoverableitemsroot",
1954
+ "recoverableitemspurges",
1955
+ "recoverableitemsversions"
1956
+ ]);
1957
+ var DEFAULT_CONFIG = {
1958
+ deliveryDelayMs: 1e3,
1959
+ strictAuth: false,
1960
+ applicationAccessPolicies: {}
1961
+ };
1962
+ var USER_PURPOSE = { shared: "shared", room: "room" };
1963
+ function defaultSettings(kind) {
1964
+ return {
1965
+ archiveFolder: "",
1966
+ timeZone: "UTC",
1967
+ automaticRepliesSetting: {
1968
+ status: "disabled",
1969
+ externalAudience: "all",
1970
+ internalReplyMessage: "",
1971
+ externalReplyMessage: "",
1972
+ scheduledStartDateTime: { dateTime: "2026-01-01T00:00:00.0000000", timeZone: "UTC" },
1973
+ scheduledEndDateTime: { dateTime: "2026-01-02T00:00:00.0000000", timeZone: "UTC" }
1974
+ },
1975
+ language: { locale: "en-US", displayName: "English (United States)" },
1976
+ dateFormat: "M/d/yyyy",
1977
+ timeFormat: "h:mm tt",
1978
+ delegateMeetingMessageDeliveryOptions: "sendToDelegateOnly",
1979
+ userPurpose: USER_PURPOSE[kind] ?? "user",
1980
+ unset: ["timeZone", "language", "dateFormat", "timeFormat", "workingHours"],
1981
+ workingHours: {
1982
+ daysOfWeek: ["monday", "tuesday", "wednesday", "thursday", "friday"],
1983
+ startTime: "08:00:00.0000000",
1984
+ endTime: "17:00:00.0000000",
1985
+ timeZone: { name: "UTC" }
1986
+ }
1987
+ };
1988
+ }
1989
+ function isoSeconds(d = /* @__PURE__ */ new Date()) {
1990
+ return d.toISOString().replace(MILLISECONDS_SUFFIX, "Z");
1991
+ }
1992
+ function guidHex() {
1993
+ return randomBytes2(16).toString("hex");
1994
+ }
1995
+ var Exchange = class _Exchange {
1996
+ store;
1997
+ constructor(store) {
1998
+ this.store = store;
1999
+ }
2000
+ /**
2001
+ * Evaluates a search folder's query. Installed by the OData layer so the
2002
+ * store does not depend on the filter engine.
2003
+ */
2004
+ static searchResolver = null;
2005
+ get state() {
2006
+ let st = this.store.getData(STATE_KEY);
2007
+ if (!st) {
2008
+ st = {
2009
+ version: 1,
2010
+ tenantId: "",
2011
+ domain: "",
2012
+ mailboxes: {},
2013
+ byAddress: {},
2014
+ permissions: [],
2015
+ subscriptions: [],
2016
+ uploadSessions: {},
2017
+ pending: [],
2018
+ config: { ...DEFAULT_CONFIG }
2019
+ };
2020
+ this.store.setData(STATE_KEY, st);
2021
+ }
2022
+ return st;
2023
+ }
2024
+ get config() {
2025
+ return this.state.config;
2026
+ }
2027
+ // ---------------------------------------------------------------------------
2028
+ // Mailboxes
2029
+ // ---------------------------------------------------------------------------
2030
+ ensureMailbox(init) {
2031
+ const key = init.address.toLowerCase();
2032
+ const existing = this.state.byAddress[key];
2033
+ if (existing) {
2034
+ const mbx2 = this.state.mailboxes[existing];
2035
+ mbx2.displayName = init.displayName;
2036
+ return mbx2;
2037
+ }
2038
+ const guid = randomUUID();
2039
+ const mbx = {
2040
+ guid,
2041
+ address: init.address,
2042
+ displayName: init.displayName,
2043
+ userOid: init.userOid,
2044
+ kind: init.kind,
2045
+ store: { ...newStoreIdentity(guid), ...init.standardIds ? { standardIds: true } : {} },
2046
+ nextCounter: 65536 + (randomBytes2(2).readUInt16BE(0) & 4095),
2047
+ changeNumber: 1048576 + (randomBytes2(2).readUInt16BE(0) & 65535),
2048
+ folders: {},
2049
+ items: {},
2050
+ issued: {},
2051
+ immutable: {},
2052
+ journal: [],
2053
+ journalSeq: 0,
2054
+ settings: defaultSettings(init.kind),
2055
+ categories: defaultCategories(),
2056
+ rules: [],
2057
+ overrides: []
2058
+ };
2059
+ this.state.mailboxes[guid] = mbx;
2060
+ this.state.byAddress[key] = guid;
2061
+ this.provisionTree(mbx, DEFAULT_TREE, null);
2062
+ return mbx;
2063
+ }
2064
+ provisionTree(mbx, t, parent) {
2065
+ const counter = this.alloc(mbx);
2066
+ const folderClass = t.folderClass ?? "IPF.Note";
2067
+ mbx.folders[counter] = {
2068
+ counter,
2069
+ parentCounter: parent,
2070
+ displayName: t.name,
2071
+ wellKnownName: t.wellKnownName ?? null,
2072
+ folderClass,
2073
+ isHidden: t.isHidden ?? false,
2074
+ isMailFolder: folderClass === "IPF.Note" || folderClass === "IPF",
2075
+ distinguished: true,
2076
+ baseSize: t.baseSize ?? 0,
2077
+ changeNumber: this.bump(mbx),
2078
+ createdDateTime: isoSeconds()
2079
+ };
2080
+ mbx.issued[counter] = "folder";
2081
+ for (const c of t.children ?? []) {
2082
+ this.provisionTree(mbx, c, counter);
2083
+ }
2084
+ }
2085
+ /** Move a mailbox to a new primary address (the directory user's mail changed). */
2086
+ readdressMailbox(mbx, address) {
2087
+ delete this.state.byAddress[mbx.address.toLowerCase()];
2088
+ mbx.address = address;
2089
+ this.state.byAddress[address.toLowerCase()] = mbx.guid;
2090
+ }
2091
+ /** Remove a mailbox and the work queued for it (its directory user was deleted or lost their mail address). */
2092
+ removeMailbox(mbx) {
2093
+ delete this.state.mailboxes[mbx.guid];
2094
+ if (this.state.byAddress[mbx.address.toLowerCase()] === mbx.guid) {
2095
+ delete this.state.byAddress[mbx.address.toLowerCase()];
2096
+ }
2097
+ this.state.pending = this.state.pending.filter((job) => job.mailboxGuid !== mbx.guid);
2098
+ }
2099
+ mailboxByAddress(address) {
2100
+ const guid = this.state.byAddress[address.toLowerCase()];
2101
+ return guid ? this.state.mailboxes[guid] : void 0;
2102
+ }
2103
+ mailboxByGuid(guid) {
2104
+ return this.state.mailboxes[guid];
2105
+ }
2106
+ mailboxByOid(oid) {
2107
+ const lower = oid.toLowerCase();
2108
+ return Object.values(this.state.mailboxes).find((m) => m.userOid.toLowerCase() === lower);
2109
+ }
2110
+ allMailboxes() {
2111
+ return Object.values(this.state.mailboxes);
2112
+ }
2113
+ // ---------------------------------------------------------------------------
2114
+ // Counters and identifiers
2115
+ // ---------------------------------------------------------------------------
2116
+ alloc(mbx) {
2117
+ const c = mbx.nextCounter;
2118
+ mbx.nextCounter += 1 + (randomBytes2(1)[0] & 3);
2119
+ return c;
2120
+ }
2121
+ bump(mbx) {
2122
+ mbx.changeNumber += 1 + (randomBytes2(1)[0] & 7);
2123
+ return mbx.changeNumber;
2124
+ }
2125
+ folderId(mbx, f) {
2126
+ return encodeFolderId(mbx.store, typeof f === "number" ? f : f.counter);
2127
+ }
2128
+ itemId(mbx, item, immutable = false) {
2129
+ if (immutable) {
2130
+ return encodeImmutableId(mbx.store, item.immutableCounter);
2131
+ }
2132
+ return encodeItemId(mbx.store, item.folderCounter, item.counter);
2133
+ }
2134
+ conversationId(mbx, item) {
2135
+ return encodeConversationId(mbx.guid, Buffer.from(item.conversationGuid, "hex"));
2136
+ }
2137
+ changeKey(mbx, item) {
2138
+ const b = Buffer.from(encodeChangeKey(mbx.store, item.changeNumber, "item"), "base64");
2139
+ b.writeUInt32LE(item.changeKeyClass, 0);
2140
+ return b.toString("base64");
2141
+ }
2142
+ folderChangeKey(mbx, f) {
2143
+ return encodeChangeKey(mbx.store, f.changeNumber, "folder");
2144
+ }
2145
+ // ---------------------------------------------------------------------------
2146
+ // Folder lookup
2147
+ // ---------------------------------------------------------------------------
2148
+ folder(mbx, counter) {
2149
+ return mbx.folders[counter];
2150
+ }
2151
+ wellKnown(mbx, name) {
2152
+ const f = Object.values(mbx.folders).find((x2) => x2.wellKnownName === name);
2153
+ if (!f) {
2154
+ throw new Error(`mailbox has no ${name} folder`);
2155
+ }
2156
+ return f;
2157
+ }
2158
+ root(mbx) {
2159
+ return Object.values(mbx.folders).find((f) => f.parentCounter === null);
2160
+ }
2161
+ /**
2162
+ * Resolve a folder reference from a URL or body: a well-known name
2163
+ * (case-insensitive) or a REST id.
2164
+ */
2165
+ /**
2166
+ * Resolve a folder id or well-known name. Under /mailFolders the calendar,
2167
+ * contacts and tasks names are not mail folders: Graph answers 404
2168
+ * ErrorItemNotFound (verified live). Contact and calendar routes pass
2169
+ * `nonMail` to accept them.
2170
+ */
2171
+ resolveFolder(mbx, ref, nonMail = false) {
2172
+ const lower = ref.toLowerCase();
2173
+ if (ABSENT_WELL_KNOWN.has(lower)) {
2174
+ throw Errors.folderNotFound();
2175
+ }
2176
+ if (MAIL_WELL_KNOWN.has(lower)) {
2177
+ return this.wellKnown(mbx, lower);
2178
+ }
2179
+ if (lower === "calendar" || lower === "contacts" || lower === "tasks") {
2180
+ if (!nonMail) {
2181
+ throw Errors.itemNotFound();
2182
+ }
2183
+ return this.wellKnown(mbx, lower);
2184
+ }
2185
+ const decoded = decodeId(ref);
2186
+ if (!decoded) {
2187
+ throw Errors.idMalformed();
2188
+ }
2189
+ if (decoded.kind === "immutable") {
2190
+ throw Errors.itemIdForFolderId();
2191
+ }
2192
+ if (decoded.mailboxGuid !== mbx.guid) {
2193
+ throw Errors.wrongMailbox(ref, mbx.guid);
2194
+ }
2195
+ if (decoded.databaseGuid !== mbx.store.databaseGuid) {
2196
+ throw Errors.idMalformed();
2197
+ }
2198
+ if (decoded.kind === "item") {
2199
+ throw Errors.itemIdForFolderId();
2200
+ }
2201
+ const f = mbx.folders[decoded.folderCounter];
2202
+ if (f && !f.isMailFolder && !nonMail) {
2203
+ throw Errors.itemNotFound();
2204
+ }
2205
+ if (f) {
2206
+ return f;
2207
+ }
2208
+ if (mbx.issued[decoded.folderCounter]) {
2209
+ throw Errors.itemNotFound();
2210
+ }
2211
+ throw Errors.idMalformed();
2212
+ }
2213
+ /** Resolve a message id (regular or immutable). */
2214
+ resolveItem(mbx, ref) {
2215
+ const decoded = decodeId(ref);
2216
+ if (!decoded) {
2217
+ throw Errors.idMalformed();
2218
+ }
2219
+ if (decoded.kind === "immutable") {
2220
+ if (decoded.databaseGuid !== mbx.store.databaseGuid) {
2221
+ const owner = this.allMailboxes().find((m) => m.store.databaseGuid === decoded.databaseGuid);
2222
+ if (owner) {
2223
+ throw Errors.wrongMailbox(ref, mbx.guid);
2224
+ }
2225
+ throw Errors.idMalformed();
2226
+ }
2227
+ const current = mbx.immutable[decoded.immutableCounter];
2228
+ if (current !== void 0 && mbx.items[current]) {
2229
+ return { item: mbx.items[current], immutable: true };
2230
+ }
2231
+ if (current !== void 0) {
2232
+ throw Errors.itemNotFound();
2233
+ }
2234
+ throw Errors.idMalformed();
2235
+ }
2236
+ if (decoded.mailboxGuid !== mbx.guid) {
2237
+ throw Errors.wrongMailbox(ref, mbx.guid);
2238
+ }
2239
+ if (decoded.databaseGuid !== mbx.store.databaseGuid) {
2240
+ throw Errors.idMalformed();
2241
+ }
2242
+ if (decoded.kind === "folder") {
2243
+ throw Errors.folderIdForItemId();
2244
+ }
2245
+ const item = mbx.items[decoded.itemCounter];
2246
+ if (item && item.folderCounter === decoded.folderCounter) {
2247
+ return { item, immutable: false };
2248
+ }
2249
+ if (mbx.issued[decoded.itemCounter]) {
2250
+ throw Errors.itemNotFound();
2251
+ }
2252
+ throw Errors.idMalformed();
2253
+ }
2254
+ /**
2255
+ * Resolve an item id against any per-mailbox item map (contacts, events).
2256
+ * Same codec and error rules as messages: wrong mailbox, malformed and
2257
+ * deleted ids are told apart.
2258
+ */
2259
+ resolveIn(mbx, ref, map) {
2260
+ const decoded = decodeId(ref);
2261
+ if (!decoded) {
2262
+ throw Errors.idMalformed();
2263
+ }
2264
+ if (decoded.kind === "immutable") {
2265
+ if (decoded.databaseGuid !== mbx.store.databaseGuid) {
2266
+ const owner = this.allMailboxes().find((m) => m.store.databaseGuid === decoded.databaseGuid);
2267
+ if (owner) {
2268
+ throw Errors.wrongMailbox(ref, mbx.guid);
2269
+ }
2270
+ throw Errors.idMalformed();
2271
+ }
2272
+ const current = mbx.immutable[decoded.immutableCounter];
2273
+ if (current !== void 0 && map[current]) {
2274
+ return { item: map[current], immutable: true };
2275
+ }
2276
+ if (current !== void 0) {
2277
+ throw Errors.itemNotFound();
2278
+ }
2279
+ throw Errors.idMalformed();
2280
+ }
2281
+ if (decoded.mailboxGuid !== mbx.guid) {
2282
+ throw Errors.wrongMailbox(ref, mbx.guid);
2283
+ }
2284
+ if (decoded.databaseGuid !== mbx.store.databaseGuid) {
2285
+ throw Errors.idMalformed();
2286
+ }
2287
+ if (decoded.kind === "folder") {
2288
+ throw Errors.folderIdForItemId();
2289
+ }
2290
+ const item = map[decoded.itemCounter];
2291
+ if (item && item.folderCounter === decoded.folderCounter) {
2292
+ return { item, immutable: false };
2293
+ }
2294
+ if (mbx.issued[decoded.itemCounter]) {
2295
+ throw Errors.itemNotFound();
2296
+ }
2297
+ throw Errors.idMalformed();
2298
+ }
2299
+ childFolders(mbx, parent, opts = {}) {
2300
+ return Object.values(mbx.folders).filter(
2301
+ (f) => f.parentCounter === parent.counter && (opts.includeHidden || !f.isHidden) && (!opts.mailOnly || f.isMailFolder || f.folderClass.startsWith("IPF.Note"))
2302
+ ).sort((a, b) => a.displayName.localeCompare(b.displayName, "en", { sensitivity: "base" }));
2303
+ }
2304
+ descendants(mbx, f) {
2305
+ const out = [];
2306
+ const walk = (p) => {
2307
+ for (const c of Object.values(mbx.folders)) {
2308
+ if (c.parentCounter === p.counter) {
2309
+ out.push(c);
2310
+ walk(c);
2311
+ }
2312
+ }
2313
+ };
2314
+ walk(f);
2315
+ return out;
2316
+ }
2317
+ itemsIn(mbx, folderCounter) {
2318
+ return Object.values(mbx.items).filter((i) => i.folderCounter === folderCounter);
2319
+ }
2320
+ /**
2321
+ * Messages visible through a folder: its own items, or for a search folder
2322
+ * the live result of its query over the source folders (verified:
2323
+ * mail.searchfolders.lifecycle).
2324
+ */
2325
+ folderItems(mbx, f) {
2326
+ if (f.search) {
2327
+ return _Exchange.searchResolver ? _Exchange.searchResolver(this, mbx, f) : [];
2328
+ }
2329
+ return this.itemsIn(mbx, f.counter);
2330
+ }
2331
+ /** Source folders a search folder looks at (with descendants when nested). */
2332
+ searchSources(mbx, f) {
2333
+ const out = /* @__PURE__ */ new Map();
2334
+ for (const c of f.search?.sourceFolderCounters ?? []) {
2335
+ const src = mbx.folders[c];
2336
+ if (!src) {
2337
+ continue;
2338
+ }
2339
+ out.set(src.counter, src);
2340
+ if (f.search?.includeNestedFolders) {
2341
+ for (const d of this.descendants(mbx, src)) {
2342
+ out.set(d.counter, d);
2343
+ }
2344
+ }
2345
+ }
2346
+ return [...out.values()].filter((x2) => !x2.search);
2347
+ }
2348
+ /** Items visible to the mailbox-wide /messages collection (all IPM mail folders). */
2349
+ mailboxItems(mbx) {
2350
+ const ipm = this.wellKnown(mbx, "msgfolderroot");
2351
+ const allowed = /* @__PURE__ */ new Set([
2352
+ ipm.counter,
2353
+ ...this.descendants(mbx, ipm).filter((f) => f.isMailFolder).map((f) => f.counter)
2354
+ ]);
2355
+ return Object.values(mbx.items).filter((i) => allowed.has(i.folderCounter));
2356
+ }
2357
+ otherItemsIn(mbx, folderCounter) {
2358
+ const contacts = Object.values(mbx.contacts ?? {}).filter((x2) => x2.folderCounter === folderCounter).length;
2359
+ const events = Object.values(mbx.events ?? {}).filter((x2) => x2.folderCounter === folderCounter).length;
2360
+ return contacts + events;
2361
+ }
2362
+ counts(mbx, f) {
2363
+ const items = this.folderItems(mbx, f);
2364
+ return {
2365
+ childFolderCount: Object.values(mbx.folders).filter((c) => c.parentCounter === f.counter).length,
2366
+ unreadItemCount: items.filter((i) => !i.isRead).length,
2367
+ // Verified live (contacts.delete-target): a deleted contact counts in Deleted Items, so
2368
+ // contacts and calendar items stored in a mail folder count alongside its messages.
2369
+ totalItemCount: items.length + (f.search ? 0 : this.otherItemsIn(mbx, f.counter)),
2370
+ // Verified: search folders report sizeInBytes null.
2371
+ sizeInBytes: f.search ? null : f.baseSize + items.reduce((n, i) => n + i.size, 0)
2372
+ };
2373
+ }
2374
+ // ---------------------------------------------------------------------------
2375
+ // Journal (drives delta queries and change notifications)
2376
+ // ---------------------------------------------------------------------------
2377
+ listeners = [];
2378
+ /** Listen for journal entries; returns a function that stops listening. */
2379
+ onChange(fn) {
2380
+ this.listeners.push(fn);
2381
+ return () => {
2382
+ this.listeners = this.listeners.filter((l) => l !== fn);
2383
+ };
2384
+ }
2385
+ journal(mbx, kind, entity, folderCounter, counter, formerId) {
2386
+ mbx.journalSeq += 1;
2387
+ mbx.journal.push({
2388
+ seq: mbx.journalSeq,
2389
+ at: (/* @__PURE__ */ new Date()).toISOString(),
2390
+ kind,
2391
+ entity,
2392
+ folderCounter,
2393
+ counter,
2394
+ formerId
2395
+ });
2396
+ for (const l of this.listeners) {
2397
+ try {
2398
+ l(mbx, kind, entity, counter, folderCounter, formerId);
2399
+ } catch {
2400
+ }
2401
+ }
2402
+ }
2403
+ // ---------------------------------------------------------------------------
2404
+ // Folder operations
2405
+ // ---------------------------------------------------------------------------
2406
+ assertUniqueName(mbx, parent, name, except) {
2407
+ const lower = name.toLowerCase();
2408
+ return !Object.values(mbx.folders).some(
2409
+ (f) => f.parentCounter === parent.counter && f.counter !== except && f.displayName.toLowerCase() === lower
2410
+ );
2411
+ }
2412
+ createFolder(mbx, parent, input) {
2413
+ if (!input.displayName.trim()) {
2414
+ throw Errors.folderSaveProperty();
2415
+ }
2416
+ if (!this.assertUniqueName(mbx, parent, input.displayName)) {
2417
+ throw Errors.folderExists(input.displayName);
2418
+ }
2419
+ const counter = this.alloc(mbx);
2420
+ const f = {
2421
+ counter,
2422
+ parentCounter: parent.counter,
2423
+ displayName: input.displayName,
2424
+ wellKnownName: null,
2425
+ folderClass: input.folderClass ?? "IPF.Note",
2426
+ isHidden: input.isHidden ?? false,
2427
+ isMailFolder: (input.folderClass ?? "IPF.Note").startsWith("IPF.Note"),
2428
+ distinguished: false,
2429
+ baseSize: 0,
2430
+ changeNumber: this.bump(mbx),
2431
+ createdDateTime: isoSeconds(),
2432
+ ...input.search ? { search: input.search } : {}
2433
+ };
2434
+ mbx.folders[counter] = f;
2435
+ mbx.issued[counter] = "folder";
2436
+ this.journal(mbx, "created", "folder", parent.counter, counter);
2437
+ return f;
2438
+ }
2439
+ updateFolder(mbx, f, patch) {
2440
+ if (patch.displayName !== void 0) {
2441
+ if (f.distinguished) {
2442
+ throw Errors.folderSaveProperty();
2443
+ }
2444
+ if (!patch.displayName.trim()) {
2445
+ throw Errors.folderSaveProperty();
2446
+ }
2447
+ const parent = f.parentCounter === null ? null : mbx.folders[f.parentCounter];
2448
+ if (parent && !this.assertUniqueName(mbx, parent, patch.displayName, f.counter)) {
2449
+ throw Errors.folderSaveProperty();
2450
+ }
2451
+ f.displayName = patch.displayName;
2452
+ }
2453
+ if (patch.isHidden !== void 0) {
2454
+ f.isHidden = patch.isHidden;
2455
+ }
2456
+ f.changeNumber = this.bump(mbx);
2457
+ this.journal(mbx, "updated", "folder", f.parentCounter ?? f.counter, f.counter);
2458
+ return f;
2459
+ }
2460
+ deleteFolder(mbx, f) {
2461
+ if (f.distinguished) {
2462
+ throw Errors.deleteDistinguished();
2463
+ }
2464
+ const formerParent = f.parentCounter ?? f.counter;
2465
+ for (const d of [f, ...this.descendants(mbx, f)]) {
2466
+ for (const item of this.itemsIn(mbx, d.counter)) {
2467
+ this.removeItem(mbx, item, "deleted");
2468
+ }
2469
+ for (const [entity, map] of [
2470
+ ["contact", mbx.contacts],
2471
+ ["event", mbx.events]
2472
+ ]) {
2473
+ for (const x2 of Object.values(map ?? {})) {
2474
+ if (x2.folderCounter !== d.counter) {
2475
+ continue;
2476
+ }
2477
+ delete map[x2.counter];
2478
+ mbx.immutable[x2.immutableCounter] = -1;
2479
+ this.journal(mbx, "deleted", entity, d.counter, x2.counter, encodeItemId(mbx.store, d.counter, x2.counter));
2480
+ }
2481
+ }
2482
+ delete mbx.folders[d.counter];
2483
+ }
2484
+ this.journal(mbx, "deleted", "folder", formerParent, f.counter, this.folderId(mbx, f));
2485
+ }
2486
+ moveFolder(mbx, f, dest) {
2487
+ if (f.distinguished) {
2488
+ throw Errors.moveCopyFailed();
2489
+ }
2490
+ if (dest.counter === f.counter || this.descendants(mbx, f).some((d) => d.counter === dest.counter)) {
2491
+ throw Errors.moveCopyFailed();
2492
+ }
2493
+ if (!this.assertUniqueName(mbx, dest, f.displayName, f.counter)) {
2494
+ throw Errors.folderExists(f.displayName);
2495
+ }
2496
+ const from = f.parentCounter;
2497
+ f.parentCounter = dest.counter;
2498
+ f.changeNumber = this.bump(mbx);
2499
+ this.journal(mbx, "deleted", "folder", from, f.counter, this.folderId(mbx, f));
2500
+ this.journal(mbx, "created", "folder", dest.counter, f.counter);
2501
+ return f;
2502
+ }
2503
+ copyFolder(mbx, f, dest) {
2504
+ if (f.distinguished) {
2505
+ throw Errors.moveCopyFailed();
2506
+ }
2507
+ if (dest.counter === f.counter || this.descendants(mbx, f).some((d) => d.counter === dest.counter)) {
2508
+ throw Errors.moveCopyFailed();
2509
+ }
2510
+ if (!this.assertUniqueName(mbx, dest, f.displayName)) {
2511
+ throw Errors.folderExists(f.displayName);
2512
+ }
2513
+ const copyTree = (src, parent) => {
2514
+ const counter = this.alloc(mbx);
2515
+ const c = {
2516
+ ...src,
2517
+ counter,
2518
+ parentCounter: parent.counter,
2519
+ distinguished: false,
2520
+ wellKnownName: null,
2521
+ changeNumber: this.bump(mbx)
2522
+ };
2523
+ mbx.folders[counter] = c;
2524
+ mbx.issued[counter] = "folder";
2525
+ for (const item of this.itemsIn(mbx, src.counter)) {
2526
+ this.copyItem(mbx, item, c);
2527
+ }
2528
+ for (const child of Object.values(mbx.folders).filter(
2529
+ (x2) => x2.parentCounter === src.counter && x2.counter !== counter
2530
+ )) {
2531
+ copyTree(child, c);
2532
+ }
2533
+ return c;
2534
+ };
2535
+ const copy = copyTree(f, dest);
2536
+ this.journal(mbx, "created", "folder", dest.counter, copy.counter);
2537
+ return copy;
2538
+ }
2539
+ // ---------------------------------------------------------------------------
2540
+ // Items
2541
+ // ---------------------------------------------------------------------------
2542
+ /** Directory display name for an address inside the tenant. */
2543
+ directoryName(address) {
2544
+ const mbx = this.mailboxByAddress(address);
2545
+ return mbx ? mbx.displayName : null;
2546
+ }
2547
+ /**
2548
+ * Normalize a recipient the way the store does: tenant addresses take the
2549
+ * directory display name; external addresses keep the supplied name or
2550
+ * fall back to the address.
2551
+ */
2552
+ resolveRecipient(r) {
2553
+ const address = r.emailAddress?.address ?? "";
2554
+ const dir = address ? this.directoryName(address) : null;
2555
+ const name = dir ?? (r.emailAddress?.name || address);
2556
+ return { emailAddress: { name, address } };
2557
+ }
2558
+ newMessage(mbx, folder, fields) {
2559
+ const now = isoSeconds();
2560
+ const counter = this.alloc(mbx);
2561
+ const conversationGuid = fields.conversationGuid ?? guidHex();
2562
+ const item = {
2563
+ counter,
2564
+ immutableCounter: counter,
2565
+ folderCounter: folder.counter,
2566
+ state: "draft",
2567
+ itemClass: "IPM.Note",
2568
+ createdDateTime: now,
2569
+ lastModifiedDateTime: now,
2570
+ receivedDateTime: now,
2571
+ sentDateTime: now,
2572
+ changeNumber: this.bump(mbx),
2573
+ changeKeyClass: 9,
2574
+ categories: [],
2575
+ internetMessageId: "",
2576
+ subject: "",
2577
+ body: { contentType: "text", content: "" },
2578
+ uniqueBody: null,
2579
+ importance: "normal",
2580
+ conversationGuid,
2581
+ conversationIndex: "",
2582
+ isDeliveryReceiptRequested: false,
2583
+ isReadReceiptRequested: false,
2584
+ isRead: true,
2585
+ inferenceClassification: "focused",
2586
+ sender: null,
2587
+ from: null,
2588
+ toRecipients: [],
2589
+ ccRecipients: [],
2590
+ bccRecipients: [],
2591
+ replyTo: [],
2592
+ flag: { flagStatus: "notFlagged" },
2593
+ internetMessageHeaders: null,
2594
+ attachments: [],
2595
+ inReplyTo: null,
2596
+ references: null,
2597
+ readReceiptSent: false,
2598
+ singleValueExtendedProperties: [],
2599
+ multiValueExtendedProperties: [],
2600
+ extensions: [],
2601
+ size: 0,
2602
+ ...fields
2603
+ };
2604
+ item.counter = counter;
2605
+ item.immutableCounter = counter;
2606
+ item.folderCounter = folder.counter;
2607
+ item.changeNumber = this.bump(mbx);
2608
+ this.recomputeSize(item);
2609
+ mbx.items[counter] = item;
2610
+ mbx.issued[counter] = "item";
2611
+ mbx.immutable[counter] = counter;
2612
+ this.journal(mbx, "created", "message", folder.counter, counter);
2613
+ return item;
2614
+ }
2615
+ recomputeSize(item) {
2616
+ const recips = [...item.toRecipients, ...item.ccRecipients, ...item.bccRecipients, ...item.replyTo];
2617
+ item.size = 1800 + Buffer.byteLength(item.subject) * 2 + Buffer.byteLength(item.body.content) + recips.reduce((n, r) => n + 40 + r.emailAddress.address.length + r.emailAddress.name.length, 0) + item.attachments.reduce((n, a) => n + a.size, 0);
2618
+ }
2619
+ /** Record a content change: new change number, lastModified, journal. */
2620
+ touch(mbx, item, kind = "content") {
2621
+ if (kind === "content") {
2622
+ item.changeNumber = this.bump(mbx);
2623
+ item.lastModifiedDateTime = isoSeconds();
2624
+ this.recomputeSize(item);
2625
+ this.journal(mbx, "updated", "message", item.folderCounter, item.counter);
2626
+ } else {
2627
+ this.journal(mbx, "read", "message", item.folderCounter, item.counter);
2628
+ }
2629
+ }
2630
+ moveItem(mbx, item, dest) {
2631
+ if (dest.counter === item.folderCounter) {
2632
+ return item;
2633
+ }
2634
+ const formerId = this.itemId(mbx, item);
2635
+ const from = item.folderCounter;
2636
+ delete mbx.items[item.counter];
2637
+ this.journal(mbx, "deleted", "message", from, item.counter, formerId);
2638
+ const counter = this.alloc(mbx);
2639
+ item.counter = counter;
2640
+ item.folderCounter = dest.counter;
2641
+ item.changeNumber = this.bump(mbx);
2642
+ item.lastModifiedDateTime = isoSeconds();
2643
+ mbx.items[counter] = item;
2644
+ mbx.issued[counter] = "item";
2645
+ mbx.immutable[item.immutableCounter] = counter;
2646
+ this.journal(mbx, "created", "message", dest.counter, counter);
2647
+ return item;
2648
+ }
2649
+ copyItem(mbx, item, dest, targetMbx = mbx) {
2650
+ const clone = JSON.parse(JSON.stringify(item));
2651
+ const copy = this.newMessage(targetMbx, dest, {
2652
+ ...clone,
2653
+ createdDateTime: isoSeconds(),
2654
+ lastModifiedDateTime: isoSeconds(),
2655
+ attachments: clone.attachments.map((a) => ({ ...a, counter: this.alloc(targetMbx) }))
2656
+ });
2657
+ return copy;
2658
+ }
2659
+ /** Remove an item from its folder (journaled). */
2660
+ removeItem(mbx, item, kind) {
2661
+ const formerId = this.itemId(mbx, item);
2662
+ delete mbx.items[item.counter];
2663
+ this.journal(mbx, kind, "message", item.folderCounter, item.counter, formerId);
2664
+ }
2665
+ /** DELETE: soft delete into Recoverable Items/Deletions. */
2666
+ deleteItem(mbx, item) {
2667
+ const deletions = this.wellKnown(mbx, "recoverableitemsdeletions");
2668
+ if (item.folderCounter === deletions.counter) {
2669
+ this.purgeItem(mbx, item);
2670
+ return;
2671
+ }
2672
+ this.moveItem(mbx, item, deletions);
2673
+ }
2674
+ /** permanentDelete: remove entirely (ids now answer 404). */
2675
+ purgeItem(mbx, item) {
2676
+ this.removeItem(mbx, item, "deleted");
2677
+ delete mbx.immutable[item.immutableCounter];
2678
+ mbx.immutable[item.immutableCounter] = -1;
2679
+ }
2680
+ findByInternetMessageId(mbx, imid) {
2681
+ return Object.values(mbx.items).filter((i) => i.internetMessageId === imid);
2682
+ }
2683
+ };
2684
+ function defaultCategories() {
2685
+ const presets = [
2686
+ ["Red category", "preset0"],
2687
+ ["Orange category", "preset1"],
2688
+ ["Yellow category", "preset3"],
2689
+ ["Green category", "preset4"],
2690
+ ["Blue category", "preset7"],
2691
+ ["Purple category", "preset8"]
2692
+ ];
2693
+ return presets.map(([displayName, color]) => ({ id: randomUUID(), displayName, color }));
2694
+ }
2695
+ var instances = /* @__PURE__ */ new WeakMap();
2696
+ function getExchange(store) {
2697
+ let ex = instances.get(store);
2698
+ if (!ex) {
2699
+ ex = new Exchange(store);
2700
+ instances.set(store, ex);
2701
+ }
2702
+ return ex;
2703
+ }
2704
+
2705
+ // src/graph/exchange/calendar.ts
2706
+ var ISO_DATE_PARTS = /^(\d{4})-(\d{2})-(\d{2})$/;
2707
+ var DAY = 864e5;
2708
+ var ZERO_TIME = "0001-01-01T00:00:00Z";
2709
+ var GOID_PREFIX = "040000008200E00074C5B7101A82E008";
2710
+ function newGoid(at = Date.now()) {
2711
+ const filetime = (BigInt(at) * 10000n + 116444736000000000n).toString(16).padStart(16, "0");
2712
+ const le = filetime.match(/../g).reverse().join("");
2713
+ return `${GOID_PREFIX}00000000${le}0000000000000000${"10000000"}${randomBytes3(16).toString("hex")}`.toUpperCase();
2714
+ }
2715
+ function occurrenceGoid(uid, dateMs) {
2716
+ const d = new Date(dateMs);
2717
+ const hex = d.getUTCFullYear().toString(16).padStart(4, "0") + (d.getUTCMonth() + 1).toString(16).padStart(2, "0") + d.getUTCDate().toString(16).padStart(2, "0");
2718
+ return `${uid.slice(0, 32)}${hex.toUpperCase()}${uid.slice(40)}`;
2719
+ }
2720
+ var GROUP_CLASSES = {
2721
+ my: "0006f0b7-0000-0000-c000-000000000046",
2722
+ other: "0006f0b8-0000-0000-c000-000000000046",
2723
+ people: "0006f0b9-0000-0000-c000-000000000046"
2724
+ };
2725
+ function isCalendarFolder(f) {
2726
+ return f.folderClass.startsWith("IPF.Appointment");
2727
+ }
2728
+ function defaultCalendar(ex, mbx) {
2729
+ return ex.wellKnown(mbx, "calendar");
2730
+ }
2731
+ function calendarGroups(ex, mbx) {
2732
+ if (!mbx.calendarGroups) {
2733
+ const root = ex.wellKnown(mbx, "msgfolderroot");
2734
+ const make = (name, classId, calendars) => {
2735
+ const counter = ex.alloc(mbx);
2736
+ mbx.issued[counter] = "item";
2737
+ return {
2738
+ id: encodeItemId(mbx.store, root.counter, counter),
2739
+ name,
2740
+ classId,
2741
+ changeNumber: ex.bump(mbx),
2742
+ calendars
2743
+ };
2744
+ };
2745
+ mbx.calendarGroups = [
2746
+ make("My Calendars", GROUP_CLASSES.my, [defaultCalendar(ex, mbx).counter]),
2747
+ make("Other Calendars", GROUP_CLASSES.other, []),
2748
+ ...mbx.kind === "user" ? [make("People's Calendars", GROUP_CLASSES.people, [])] : []
2749
+ ];
2750
+ }
2751
+ return mbx.calendarGroups;
2752
+ }
2753
+ function groupOfCalendar(ex, mbx, f) {
2754
+ const groups = calendarGroups(ex, mbx);
2755
+ return groups.find((g2) => g2.calendars.includes(f.counter)) ?? groups[0];
2756
+ }
2757
+ function calendarFolders(ex, mbx) {
2758
+ const def = defaultCalendar(ex, mbx);
2759
+ const all = ex.descendants(mbx, ex.wellKnown(mbx, "msgfolderroot")).filter((f) => isCalendarFolder(f) && f.counter !== def.counter && !f.isHidden);
2760
+ return [def, ...all];
2761
+ }
2762
+ var NAV_FOLDER = 1;
2763
+ function calendarId(ex, mbx, f) {
2764
+ const m = calendarMeta(mbx, f);
2765
+ if (m.navCounter === void 0) {
2766
+ m.navCounter = ex.alloc(mbx);
2767
+ }
2768
+ return encodeItemId(mbx.store, NAV_FOLDER, m.navCounter);
2769
+ }
2770
+ function calendarByNavId(mbx, id) {
2771
+ const d = decodeId(id);
2772
+ if (d?.kind !== "item" || d.folderCounter !== NAV_FOLDER) {
2773
+ return null;
2774
+ }
2775
+ for (const [counter, m] of Object.entries(mbx.calendarMeta ?? {})) {
2776
+ if (m.navCounter === d.itemCounter) {
2777
+ return Number(counter);
2778
+ }
2779
+ }
2780
+ return null;
2781
+ }
2782
+ function calendarMeta(mbx, f) {
2783
+ if (!mbx.calendarMeta) {
2784
+ mbx.calendarMeta = {};
2785
+ }
2786
+ mbx.calendarMeta[f.counter] ??= { color: "auto", hexColor: "", isDefault: false };
2787
+ return mbx.calendarMeta[f.counter];
2788
+ }
2789
+ var CALENDAR_COLORS = {
2790
+ auto: "",
2791
+ lightBlue: "#a6d1f5",
2792
+ lightGreen: "#87d28e",
2793
+ lightOrange: "#fcab73",
2794
+ lightGray: "#c0c0c0",
2795
+ lightYellow: "#f4d07a",
2796
+ lightTeal: "#5fd2c3",
2797
+ lightPink: "#f8a9ce",
2798
+ lightBrown: "#e2b38a",
2799
+ lightRed: "#f88a8b",
2800
+ maxColor: ""
2801
+ };
2802
+ function eventsOf(mbx) {
2803
+ if (!mbx.events) {
2804
+ mbx.events = {};
2805
+ }
2806
+ return mbx.events;
2807
+ }
2808
+ function eventChangeKey(mbx, e) {
2809
+ return encodeChangeKey(mbx.store, e.changeNumber, "event");
2810
+ }
2811
+ function eventLongChangeKey(mbx, e) {
2812
+ return encodeChangeKey(mbx.store, e.changeNumber, "eventLong");
2813
+ }
2814
+ function eventId(mbx, e, immutable = false) {
2815
+ if (e.type === "exception" && e.seriesMasterCounter !== null && e.originalStartMs !== null) {
2816
+ const master = eventsOf(mbx)[e.seriesMasterCounter];
2817
+ if (master) {
2818
+ return occurrenceId(mbx, master, localDate(master, e.originalStartMs));
2819
+ }
2820
+ }
2821
+ return immutable ? encodeImmutableId(mbx.store, e.immutableCounter) : encodeItemId(mbx.store, e.folderCounter, e.counter);
2822
+ }
2823
+ function occurrenceId(mbx, master, dateMs) {
2824
+ return encodeOccurrenceId(mbx.store, master.folderCounter, master.counter, dateMs);
2825
+ }
2826
+ function occurrenceKey(mbx, master, dateMs) {
2827
+ return `OID.${eventId(mbx, master)}.${new Date(dateMs).toISOString().slice(0, 10)}`;
2828
+ }
2829
+ function eventsIn(mbx, folderCounter) {
2830
+ return Object.values(eventsOf(mbx)).filter((e) => e.folderCounter === folderCounter);
2831
+ }
2832
+ function defaultEvent(organizer) {
2833
+ const uid = newGoid();
2834
+ return {
2835
+ categories: [],
2836
+ extensions: [],
2837
+ singleValueExtendedProperties: [],
2838
+ multiValueExtendedProperties: [],
2839
+ uid,
2840
+ iCalUId: uid,
2841
+ type: "singleInstance",
2842
+ seriesMasterCounter: null,
2843
+ originalStartMs: null,
2844
+ subject: "",
2845
+ // Verified: an event created without a body holds empty text.
2846
+ body: { contentType: "text", content: "" },
2847
+ importance: "normal",
2848
+ sensitivity: "normal",
2849
+ showAs: "busy",
2850
+ isAllDay: false,
2851
+ isCancelled: false,
2852
+ isDraft: false,
2853
+ startMs: 0,
2854
+ endMs: 0,
2855
+ originalStartTimeZone: "UTC",
2856
+ originalEndTimeZone: "UTC",
2857
+ location: emptyLocation(),
2858
+ locations: [],
2859
+ attendees: [],
2860
+ organizer: { emailAddress: { ...organizer } },
2861
+ isOrganizer: true,
2862
+ responseStatus: { response: "organizer", time: ZERO_TIME },
2863
+ responseRequested: true,
2864
+ allowNewTimeProposals: true,
2865
+ hideAttendees: false,
2866
+ isReminderOn: true,
2867
+ reminderMinutesBeforeStart: 15,
2868
+ reminderDismissedMs: null,
2869
+ reminderSnoozedUntilMs: null,
2870
+ isOnlineMeeting: false,
2871
+ onlineMeetingProvider: "unknown",
2872
+ transactionId: null,
2873
+ recurrence: null,
2874
+ cancelledOccurrences: [],
2875
+ attachments: [],
2876
+ sequence: 0
2877
+ };
2878
+ }
2879
+ function emptyLocation() {
2880
+ return { displayName: "", locationType: "default", uniqueIdType: "unknown", address: {}, coordinates: {} };
2881
+ }
2882
+ function newEvent(ex, mbx, folder, init) {
2883
+ const now = createdStamp();
2884
+ const counter = ex.alloc(mbx);
2885
+ const owner = { name: mbx.displayName, address: mbx.address };
2886
+ const e = {
2887
+ ...defaultEvent(owner),
2888
+ ...init,
2889
+ counter,
2890
+ immutableCounter: counter,
2891
+ folderCounter: folder.counter,
2892
+ changeNumber: ex.bump(mbx),
2893
+ createdDateTime: now,
2894
+ lastModifiedDateTime: now
2895
+ };
2896
+ eventsOf(mbx)[counter] = e;
2897
+ mbx.issued[counter] = "item";
2898
+ mbx.immutable[counter] = counter;
2899
+ ex.journal(mbx, "created", "event", folder.counter, counter);
2900
+ return e;
2901
+ }
2902
+ function createdStamp(d = /* @__PURE__ */ new Date()) {
2903
+ return `${d.toISOString().slice(0, 23)}0000Z`;
2904
+ }
2905
+ function touchEvent(ex, mbx, e) {
2906
+ e.changeNumber = ex.bump(mbx);
2907
+ e.lastModifiedDateTime = createdStamp();
2908
+ ex.journal(mbx, "updated", "event", e.folderCounter, e.counter);
2909
+ }
2910
+ function moveEvent(ex, mbx, e, dest) {
2911
+ if (dest.counter === e.folderCounter) {
2912
+ return e;
2913
+ }
2914
+ const map = eventsOf(mbx);
2915
+ const formerId = eventId(mbx, e);
2916
+ delete map[e.counter];
2917
+ ex.journal(mbx, "deleted", "event", e.folderCounter, e.counter, formerId);
2918
+ const counter = ex.alloc(mbx);
2919
+ e.counter = counter;
2920
+ e.folderCounter = dest.counter;
2921
+ e.changeNumber = ex.bump(mbx);
2922
+ e.lastModifiedDateTime = createdStamp();
2923
+ map[counter] = e;
2924
+ mbx.issued[counter] = "item";
2925
+ mbx.immutable[e.immutableCounter] = counter;
2926
+ ex.journal(mbx, "created", "event", dest.counter, counter);
2927
+ return e;
2928
+ }
2929
+ function purgeEvent(ex, mbx, e) {
2930
+ const map = eventsOf(mbx);
2931
+ for (const x2 of exceptionsOf(mbx, e)) {
2932
+ delete map[x2.counter];
2933
+ mbx.immutable[x2.immutableCounter] = -1;
2934
+ }
2935
+ const formerId = eventId(mbx, e);
2936
+ delete map[e.counter];
2937
+ mbx.immutable[e.immutableCounter] = -1;
2938
+ ex.journal(mbx, "deleted", "event", e.folderCounter, e.counter, formerId);
2939
+ }
2940
+ function deleteEvent(ex, mbx, e, to = "recoverableitemsdeletions") {
2941
+ const dest = ex.wellKnown(mbx, to);
2942
+ if (e.folderCounter === dest.counter) {
2943
+ purgeEvent(ex, mbx, e);
2944
+ return;
2945
+ }
2946
+ const exceptions = exceptionsOf(mbx, e);
2947
+ moveEvent(ex, mbx, e, dest);
2948
+ for (const x2 of exceptions) {
2949
+ x2.seriesMasterCounter = e.counter;
2950
+ }
2951
+ }
2952
+ function exceptionsOf(mbx, master) {
2953
+ return Object.values(eventsOf(mbx)).filter((x2) => x2.type === "exception" && x2.seriesMasterCounter === master.counter);
2954
+ }
2955
+ var WEEKDAYS = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
2956
+ var INDEX = ["first", "second", "third", "fourth", "last"];
2957
+ function dateOnly(s) {
2958
+ const m = ISO_DATE_PARTS.exec(s);
2959
+ if (!m) {
2960
+ return null;
2961
+ }
2962
+ const ms = Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
2963
+ return Number.isNaN(ms) ? null : ms;
2964
+ }
2965
+ function formatDate(ms) {
2966
+ return new Date(ms).toISOString().slice(0, 10);
2967
+ }
2968
+ function localDateIn(iana, utcMs) {
2969
+ const wall = utcToWall(utcMs, iana);
2970
+ return wall - (wall % DAY + DAY) % DAY;
2971
+ }
2972
+ function seriesZone(master) {
2973
+ const name = master.recurrence?.range.recurrenceTimeZone || master.originalStartTimeZone || "UTC";
2974
+ return resolveZone(name)?.iana ?? "UTC";
2975
+ }
2976
+ function localDate(master, utcMs) {
2977
+ return localDateIn(seriesZone(master), utcMs);
2978
+ }
2979
+ function daysInMonth(y, m) {
2980
+ return new Date(Date.UTC(y, m + 1, 0)).getUTCDate();
2981
+ }
2982
+ function relativeDay(y, m, days, index) {
2983
+ const matches = [];
2984
+ for (let d = 1; d <= daysInMonth(y, m); d += 1) {
2985
+ if (days.includes(new Date(Date.UTC(y, m, d)).getUTCDay())) {
2986
+ matches.push(d);
2987
+ }
2988
+ }
2989
+ if (!matches.length) {
2990
+ return null;
2991
+ }
2992
+ const i = INDEX.indexOf(index);
2993
+ if (index === "last") {
2994
+ return matches.at(-1);
2995
+ }
2996
+ return matches[i] ?? null;
2997
+ }
2998
+ function* patternDates(r) {
2999
+ const start = dateOnly(r.range.startDate);
3000
+ const p = r.pattern;
3001
+ const interval = Math.max(1, p.interval);
3002
+ const days = p.daysOfWeek.map((d) => WEEKDAYS.indexOf(d.toLowerCase())).filter((d) => d >= 0);
3003
+ const s = new Date(start);
3004
+ switch (p.type) {
3005
+ case "daily":
3006
+ for (let k = 0; ; k += 1) {
3007
+ yield start + k * interval * DAY;
3008
+ }
3009
+ case "weekly": {
3010
+ const first = WEEKDAYS.indexOf((p.firstDayOfWeek || "sunday").toLowerCase());
3011
+ const offset = (s.getUTCDay() - first + 7) % 7;
3012
+ const weekStart = start - offset * DAY;
3013
+ const order = days.map((d) => (d - first + 7) % 7).sort((a, b) => a - b);
3014
+ if (!order.length) {
3015
+ return;
3016
+ }
3017
+ for (let w = 0; ; w += 1) {
3018
+ for (const o of order) {
3019
+ const d = weekStart + (w * interval * 7 + o) * DAY;
3020
+ if (d >= start) {
3021
+ yield d;
3022
+ }
3023
+ }
3024
+ }
3025
+ }
3026
+ case "absoluteMonthly":
3027
+ case "relativeMonthly":
3028
+ case "absoluteYearly":
3029
+ case "relativeYearly": {
3030
+ const yearly = p.type.endsWith("Yearly");
3031
+ for (let k = 0; ; k += 1) {
3032
+ let y;
3033
+ let m;
3034
+ if (yearly) {
3035
+ y = s.getUTCFullYear() + k * interval;
3036
+ m = p.month - 1;
3037
+ } else {
3038
+ const total = s.getUTCMonth() + k * interval;
3039
+ y = s.getUTCFullYear() + Math.floor(total / 12);
3040
+ m = total % 12;
3041
+ }
3042
+ let day;
3043
+ if (p.type.startsWith("absolute")) {
3044
+ day = Math.min(p.dayOfMonth, daysInMonth(y, m));
3045
+ } else {
3046
+ day = relativeDay(y, m, days, p.index);
3047
+ }
3048
+ if (day === null) {
3049
+ continue;
3050
+ }
3051
+ const d = Date.UTC(y, m, day);
3052
+ if (d >= start) {
3053
+ yield d;
3054
+ }
3055
+ if (k > 2e4) {
3056
+ return;
3057
+ }
3058
+ }
3059
+ }
3060
+ default:
3061
+ return;
3062
+ }
3063
+ }
3064
+ function expandSeries(master, fromMs, toMs, limit = 5e3) {
3065
+ const r = master.recurrence;
3066
+ if (!r) {
3067
+ return [];
3068
+ }
3069
+ const iana = seriesZone(master);
3070
+ const startWall = utcToWall(master.startMs, iana);
3071
+ const timeOfDay = (startWall % DAY + DAY) % DAY;
3072
+ const duration = master.endMs - master.startMs;
3073
+ const end = r.range.type === "endDate" ? dateOnly(r.range.endDate) : null;
3074
+ const out = [];
3075
+ let n = 0;
3076
+ let guard = 0;
3077
+ for (const d of patternDates(r)) {
3078
+ guard += 1;
3079
+ if (guard > 2e5) {
3080
+ break;
3081
+ }
3082
+ if (end !== null && d > end) {
3083
+ break;
3084
+ }
3085
+ if (r.range.type === "numbered" && n >= r.range.numberOfOccurrences) {
3086
+ break;
3087
+ }
3088
+ n += 1;
3089
+ const startMs = wallToUtc(d + timeOfDay, iana);
3090
+ if (startMs >= toMs) {
3091
+ break;
3092
+ }
3093
+ if (master.cancelledOccurrences.includes(d)) {
3094
+ continue;
3095
+ }
3096
+ const endMs = startMs + duration;
3097
+ if (endMs > fromMs || endMs === startMs && startMs >= fromMs) {
3098
+ out.push({ dateMs: d, startMs, endMs });
3099
+ }
3100
+ if (out.length >= limit) {
3101
+ break;
3102
+ }
3103
+ }
3104
+ return out;
3105
+ }
3106
+ function occurrenceOn(master, dateMs) {
3107
+ const all = expandSeries(master, dateMs - 2 * DAY, dateMs + 3 * DAY);
3108
+ return all.find((o) => o.dateMs === dateMs) ?? null;
3109
+ }
3110
+ function firstSeriesDate(r) {
3111
+ for (const d of patternDates(r)) {
3112
+ return d;
3113
+ }
3114
+ return null;
3115
+ }
3116
+ function occurrenceView(master, o) {
3117
+ return {
3118
+ ...master,
3119
+ type: "occurrence",
3120
+ seriesMasterCounter: master.counter,
3121
+ originalStartMs: o.startMs,
3122
+ startMs: o.startMs,
3123
+ endMs: o.endMs,
3124
+ recurrence: null,
3125
+ iCalUId: occurrenceGoid(master.uid, o.dateMs)
3126
+ };
3127
+ }
3128
+
3129
+ // src/graph/http.ts
3130
+ import { randomUUID as randomUUID2 } from "crypto";
3131
+ var PREFERENCE_PAIR = /^([\w.-]+)\s*=\s*"?([^"]*)"?$/;
3132
+ var ODATA_JSON = "application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false; charset=utf-8";
3133
+ var OUTLOOK_JSON = "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8";
3134
+ function json(status, body, headers = {}) {
3135
+ return { status, headers: { "Content-Type": ODATA_JSON, ...headers }, body };
3136
+ }
3137
+ function noContent(status = 204, headers = {}) {
3138
+ return { status, headers, body: null };
3139
+ }
3140
+ function errorResponse(err, req) {
3141
+ const r = renderError(err, req);
3142
+ let contentType = "application/json";
3143
+ if (err.layer === "exchange") {
3144
+ contentType = ODATA_JSON;
3145
+ } else if (err.layer === "sharepoint") {
3146
+ contentType = "application/json; charset=utf-8";
3147
+ }
3148
+ return { status: r.status, headers: { "Content-Type": contentType, ...r.headers }, body: r.body };
3149
+ }
3150
+ function newRequestId() {
3151
+ return randomUUID2();
3152
+ }
3153
+ function parseJsonBody(req, opts = {}) {
3154
+ const text = req.bodyText;
3155
+ if (!text?.trim()) {
3156
+ if (opts.allowEmpty) {
3157
+ return {};
3158
+ }
3159
+ return {};
3160
+ }
3161
+ try {
3162
+ const v = JSON.parse(text);
3163
+ if (v === null || typeof v !== "object" || Array.isArray(v)) {
3164
+ throw Errors.unreadableJson();
3165
+ }
3166
+ return v;
3167
+ } catch (e) {
3168
+ if (e instanceof GraphError) {
3169
+ throw e;
3170
+ }
3171
+ throw Errors.unreadableJson();
3172
+ }
3173
+ }
3174
+ function parsePrefer(header2) {
3175
+ const out = {
3176
+ bodyType: null,
3177
+ immutableIds: false,
3178
+ maxPageSize: null,
3179
+ applied: [],
3180
+ returnMinimal: false,
3181
+ trackChangesOnly: false,
3182
+ timeZone: null
3183
+ };
3184
+ if (!header2) {
3185
+ return out;
3186
+ }
3187
+ for (const part of header2.split(",")) {
3188
+ const p = part.trim();
3189
+ const m = PREFERENCE_PAIR.exec(p);
3190
+ if (!m) {
3191
+ if (p.toLowerCase() === "return=minimal") {
3192
+ out.returnMinimal = true;
3193
+ }
3194
+ continue;
3195
+ }
3196
+ const key = m[1].toLowerCase();
3197
+ const [, , value] = m;
3198
+ if (key === "outlook.body-content-type") {
3199
+ const v = value.toLowerCase();
3200
+ if (v !== "text" && v !== "html") {
3201
+ throw Errors.bodyContentTypeNotSupported();
3202
+ }
3203
+ out.bodyType = v;
3204
+ out.applied.push(`outlook.body-content-type="${v}"`);
3205
+ } else if (key === "idtype") {
3206
+ if (value.toLowerCase() === "immutableid") {
3207
+ out.immutableIds = true;
3208
+ out.applied.push("IdType=ImmutableId");
3209
+ }
3210
+ } else if (key === "odata.maxpagesize") {
3211
+ const n = Number.parseInt(value, 10);
3212
+ if (Number.isFinite(n) && n > 0) {
3213
+ out.maxPageSize = n;
3214
+ }
3215
+ } else if (key === "return") {
3216
+ out.returnMinimal = value.toLowerCase() === "minimal";
3217
+ } else if (key === "outlook.timezone") {
3218
+ out.timeZone = value;
3219
+ } else if (key === "odata.track-changes") {
3220
+ out.trackChangesOnly = true;
3221
+ }
3222
+ }
3223
+ return out;
3224
+ }
3225
+ function contextUserKey(key) {
3226
+ return encodeURIComponent(key).replace(/%2B/gi, "+");
3227
+ }
3228
+
3229
+ // src/graph/exchange/bodies.ts
3230
+ var CID_SCHEME = /^cid:/i;
3231
+ var HTML_BODY_CONTENT = /<body[^>]*>([\s\S]*?)<\/body>/i;
3232
+ var HTML_DOCTYPE = /<!DOCTYPE[^>]*>/i;
3233
+ var HTML_HEAD_ELEMENT = /<head[\s\S]*?<\/head>/i;
3234
+ var TRAILING_WHITESPACE_CHAR = /\s$/;
3235
+ var LEADING_SPACES = /^[ \t]+/;
3236
+ var MAILTO_SCHEME = /^mailto:/i;
3237
+ var TRAILING_SPACES = /[ \t]+$/;
3238
+ var LEADING_CRLFS = /^(\r\n)+/;
3239
+ var TRAILING_CRLFS = /(\r\n)+$/;
3240
+ var BLOCK_ELEMENT_START = /^<(div|p|ul|ol|table|blockquote|pre|h[1-6])\b/i;
3241
+ var TRAILING_WHITESPACE = /\s+$/;
3242
+ var ATTENDEE_EMPTY_BODY = '<html>\r\n<head>\r\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\r\n<meta name="Generator" content="Microsoft Exchange Server">\r\n<!-- converted from rtf -->\r\n<style><!-- .EmailQuote { margin-left: 1pt; padding-left: 4pt; border-left: #800000 2px solid; } --></style>\r\n</head>\r\n<body>\r\n<font face="Times New Roman" size="3"><span style="font-size:12pt;"><a name="BM_BEGIN"></a></span></font>\r\n</body>\r\n</html>\r\n';
3243
+ var TEXT_QUOTE_HEAD = '<html><head>\r\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="Generator" content="Microsoft Exchange Server"><style>\r\n<!--\r\n.EmailQuote\r\n {margin-left:1pt;\r\n padding-left:4pt;\r\n border-left:#800000 2px solid}\r\n-->\r\n</style></head>';
3244
+ var HTML_HEAD = '<html><head>\r\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head>';
3245
+ var PREVIEW_LIMIT = 255;
3246
+ var BLOCK_PARAGRAPH = /* @__PURE__ */ new Set(["p", "ul", "ol", "table", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote", "pre"]);
3247
+ var BLOCK_LINE = /* @__PURE__ */ new Set([
3248
+ "div",
3249
+ "li",
3250
+ "tr",
3251
+ "hr",
3252
+ "section",
3253
+ "article",
3254
+ "header",
3255
+ "footer",
3256
+ "dl",
3257
+ "dt",
3258
+ "dd",
3259
+ "center",
3260
+ "form"
3261
+ ]);
3262
+ var VOID = /* @__PURE__ */ new Set([
3263
+ "br",
3264
+ "hr",
3265
+ "img",
3266
+ "meta",
3267
+ "link",
3268
+ "input",
3269
+ "col",
3270
+ "area",
3271
+ "base",
3272
+ "wbr",
3273
+ "source",
3274
+ "track",
3275
+ "embed",
3276
+ "param"
3277
+ ]);
3278
+ var SKIP_CONTENT = /* @__PURE__ */ new Set(["head", "style", "script", "title"]);
3279
+ function tokenize(html) {
3280
+ const out = [];
3281
+ const re = /<!--[\s\S]*?-->|<!\w[^>]*>|<\/?([a-zA-Z][a-zA-Z0-9:-]*)((?:\s+[^\s"'>/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'>]+))?)*)\s*(\/?)>/g;
3282
+ let last = 0;
3283
+ for (let m = re.exec(html); m; m = re.exec(html)) {
3284
+ if (m.index > last) {
3285
+ out.push({ type: "text", raw: html.slice(last, m.index) });
3286
+ }
3287
+ const [raw] = m;
3288
+ if (raw.startsWith("<!--")) {
3289
+ out.push({ type: "comment", raw });
3290
+ } else if (raw.startsWith("<!")) {
3291
+ out.push({ type: "doctype", raw });
3292
+ } else {
3293
+ const name = m[1].toLowerCase();
3294
+ const attrs = [];
3295
+ const attrRe = /([^\s"'>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/g;
3296
+ for (let a = attrRe.exec(m[2] ?? ""); a; a = attrRe.exec(m[2] ?? "")) {
3297
+ const value = a[2] ?? a[3] ?? a[4];
3298
+ attrs.push([a[1], value === void 0 ? null : value]);
3299
+ }
3300
+ out.push({ type: raw.startsWith("</") ? "close" : "open", name, attrs, selfClosing: m[3] === "/", raw });
3301
+ }
3302
+ last = re.lastIndex;
3303
+ }
3304
+ if (last < html.length) {
3305
+ out.push({ type: "text", raw: html.slice(last) });
3306
+ }
3307
+ return out;
3308
+ }
3309
+ var NAMED_ENTITIES = {
3310
+ amp: "&",
3311
+ lt: "<",
3312
+ gt: ">",
3313
+ quot: '"',
3314
+ apos: "'",
3315
+ nbsp: "\xA0",
3316
+ copy: "\xA9",
3317
+ reg: "\xAE",
3318
+ hellip: "\u2026",
3319
+ mdash: "\u2014",
3320
+ ndash: "\u2013",
3321
+ lsquo: "\u2018",
3322
+ rsquo: "\u2019",
3323
+ ldquo: "\u201C",
3324
+ rdquo: "\u201D",
3325
+ euro: "\u20AC",
3326
+ trade: "\u2122",
3327
+ bull: "\u2022"
3328
+ };
3329
+ function decodeEntities(s) {
3330
+ return s.replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi, (whole, e) => {
3331
+ if (e[0] === "#") {
3332
+ const code = e[1] === "x" || e[1] === "X" ? Number.parseInt(e.slice(2), 16) : Number.parseInt(e.slice(1), 10);
3333
+ return Number.isFinite(code) ? String.fromCodePoint(code) : whole;
3334
+ }
3335
+ return NAMED_ENTITIES[e.toLowerCase()] ?? whole;
3336
+ });
3337
+ }
3338
+ function escapeHtml(s) {
3339
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3340
+ }
3341
+ function escapeAttr(s) {
3342
+ return s.replace(/&(?![a-z#0-9]+;)/gi, "&amp;").replace(/"/g, "&quot;");
3343
+ }
3344
+ function serializeTag(t) {
3345
+ if (t.type !== "open" && t.type !== "close") {
3346
+ return t.raw;
3347
+ }
3348
+ if (t.type === "close") {
3349
+ return `</${t.name}>`;
3350
+ }
3351
+ const attrs = (t.attrs ?? []).map(([k, v]) => v === null ? ` ${k}` : ` ${k}="${escapeAttr(v)}"`).join("");
3352
+ return `<${t.name}${attrs}${t.selfClosing && !VOID.has(t.name) ? " /" : ""}>`;
3353
+ }
3354
+ function normalizeInnerHtml(inner) {
3355
+ return tokenize(inner).map((t) => {
3356
+ if (t.type !== "open") {
3357
+ return t.raw;
3358
+ }
3359
+ if (t.name === "img") {
3360
+ const src = t.attrs?.find(([k]) => k.toLowerCase() === "src")?.[1] ?? "";
3361
+ if (CID_SCHEME.test(src) && !t.attrs.some(([k]) => k.toLowerCase() === "data-outlook-trace")) {
3362
+ const idx = t.attrs.findIndex(([k]) => k.toLowerCase() === "src");
3363
+ t.attrs.splice(idx, 0, ["data-outlook-trace", "F:0|T:1"]);
3364
+ }
3365
+ }
3366
+ return serializeTag(t);
3367
+ }).join("");
3368
+ }
3369
+ function bodyInner(html) {
3370
+ const m = HTML_BODY_CONTENT.exec(html);
3371
+ if (m) {
3372
+ return m[1];
3373
+ }
3374
+ return html.replace(HTML_DOCTYPE, "").replace(/<\/?html[^>]*>/gi, "").replace(HTML_HEAD_ELEMENT, "");
3375
+ }
3376
+ function endsWithInlineContent(inner) {
3377
+ const tokens = tokenize(inner).filter((t) => !(t.type === "text" && t.raw.trim() === ""));
3378
+ const last = tokens.at(-1);
3379
+ if (!last) {
3380
+ return false;
3381
+ }
3382
+ if (last.type === "text") {
3383
+ return true;
3384
+ }
3385
+ if (last.type === "close") {
3386
+ return !(BLOCK_PARAGRAPH.has(last.name) || BLOCK_LINE.has(last.name));
3387
+ }
3388
+ if (last.type === "open") {
3389
+ return last.name === "br" || last.name === "img";
3390
+ }
3391
+ return false;
3392
+ }
3393
+ function storeHtml(content) {
3394
+ let inner = normalizeInnerHtml(bodyInner(content));
3395
+ if (endsWithInlineContent(inner) && !TRAILING_WHITESPACE_CHAR.test(inner)) {
3396
+ inner += " ";
3397
+ }
3398
+ return `${HTML_HEAD}<body>${inner}</body></html>`;
3399
+ }
3400
+ function storeBody(input) {
3401
+ if (!input) {
3402
+ return { contentType: "text", content: "" };
3403
+ }
3404
+ const type = (input.contentType ?? "text").toLowerCase() === "html" ? "html" : "text";
3405
+ const content = input.content ?? "";
3406
+ if (type === "html") {
3407
+ return { contentType: "html", content: storeHtml(content) };
3408
+ }
3409
+ return { contentType: "text", content };
3410
+ }
3411
+ function htmlToText(html, opts = { links: true }) {
3412
+ const tokens = tokenize(bodyInner(html));
3413
+ let out = "";
3414
+ let pending = 0;
3415
+ let pendingFromBr = false;
3416
+ let skipDepth = 0;
3417
+ const anchorStack = [];
3418
+ let preDepth = 0;
3419
+ let atLineStart = true;
3420
+ const requestBreak = (n) => {
3421
+ if (out.length === 0) {
3422
+ return;
3423
+ }
3424
+ if (pendingFromBr && pending > 0) {
3425
+ pending = Math.max(pending, n);
3426
+ } else {
3427
+ pending = Math.max(pending, n);
3428
+ }
3429
+ pendingFromBr = false;
3430
+ };
3431
+ const write = (text, verbatim = false) => {
3432
+ let s = text;
3433
+ if (!s) {
3434
+ return;
3435
+ }
3436
+ if (pending > 0 && out.length > 0) {
3437
+ out += "\n".repeat(pending);
3438
+ atLineStart = true;
3439
+ }
3440
+ pending = 0;
3441
+ pendingFromBr = false;
3442
+ if (atLineStart && !verbatim) {
3443
+ s = s.replace(LEADING_SPACES, "");
3444
+ }
3445
+ if (!s) {
3446
+ return;
3447
+ }
3448
+ out += s;
3449
+ atLineStart = false;
3450
+ };
3451
+ for (const t of tokens) {
3452
+ if (t.type === "comment" || t.type === "doctype") {
3453
+ continue;
3454
+ }
3455
+ if (t.type === "open" || t.type === "close") {
3456
+ const name = t.name;
3457
+ if (SKIP_CONTENT.has(name)) {
3458
+ if (t.type === "open" && !t.selfClosing) {
3459
+ skipDepth += 1;
3460
+ } else if (t.type === "close") {
3461
+ skipDepth = Math.max(0, skipDepth - 1);
3462
+ }
3463
+ continue;
3464
+ }
3465
+ if (skipDepth) {
3466
+ continue;
3467
+ }
3468
+ if (t.type === "open") {
3469
+ if (name === "br") {
3470
+ if (out.length === 0) {
3471
+ continue;
3472
+ }
3473
+ if (pending > 0 && !pendingFromBr) {
3474
+ pending = Math.max(pending, 2);
3475
+ } else {
3476
+ pending += 1;
3477
+ pendingFromBr = true;
3478
+ }
3479
+ continue;
3480
+ }
3481
+ if (name === "hr") {
3482
+ requestBreak(1);
3483
+ if (out.length === 0) {
3484
+ out = "_".repeat(32);
3485
+ } else {
3486
+ write("_".repeat(32));
3487
+ }
3488
+ atLineStart = false;
3489
+ pending = 1;
3490
+ pendingFromBr = false;
3491
+ continue;
3492
+ }
3493
+ if (BLOCK_PARAGRAPH.has(name)) {
3494
+ requestBreak(2);
3495
+ } else if (BLOCK_LINE.has(name)) {
3496
+ requestBreak(1);
3497
+ }
3498
+ if (name === "pre") {
3499
+ preDepth += 1;
3500
+ }
3501
+ if (name === "li") {
3502
+ requestBreak(1);
3503
+ write(" * ", true);
3504
+ atLineStart = false;
3505
+ }
3506
+ if (name === "a") {
3507
+ anchorStack.push(t.attrs?.find(([k]) => k.toLowerCase() === "href")?.[1] ?? null);
3508
+ }
3509
+ if (name === "td" || name === "th") {
3510
+ write(" ");
3511
+ }
3512
+ if (name === "img") {
3513
+ const alt = t.attrs?.find(([k]) => k.toLowerCase() === "alt")?.[1];
3514
+ if (alt) {
3515
+ write(`[${alt}]`);
3516
+ }
3517
+ }
3518
+ } else {
3519
+ if (BLOCK_PARAGRAPH.has(name)) {
3520
+ requestBreak(2);
3521
+ } else if (BLOCK_LINE.has(name)) {
3522
+ requestBreak(1);
3523
+ }
3524
+ if (name === "pre") {
3525
+ preDepth = Math.max(0, preDepth - 1);
3526
+ }
3527
+ if (name === "a") {
3528
+ const href = anchorStack.pop();
3529
+ if (opts.links && href && !MAILTO_SCHEME.test(href) && !href.startsWith("#")) {
3530
+ write(`<${href}>`);
3531
+ }
3532
+ }
3533
+ }
3534
+ continue;
3535
+ }
3536
+ if (skipDepth) {
3537
+ continue;
3538
+ }
3539
+ let text = decodeEntities(t.raw);
3540
+ if (!preDepth) {
3541
+ text = text.replace(/[\r\n\t ]+/g, " ");
3542
+ if (text === " " && (atLineStart || pending > 0)) {
3543
+ continue;
3544
+ }
3545
+ }
3546
+ write(text);
3547
+ }
3548
+ return out.replace(/ /g, " ").split("\n").map((l) => l.replace(TRAILING_SPACES, "")).join("\r\n").replace(LEADING_CRLFS, "").replace(TRAILING_CRLFS, "");
3549
+ }
3550
+ function bodyAsText(body) {
3551
+ if (body.contentType === "text") {
3552
+ const t2 = body.content.replace(/\r?\n/g, "\r\n");
3553
+ return { contentType: "text", content: t2.endsWith("\r\n") ? t2 : `${t2}\r
3554
+ ` };
3555
+ }
3556
+ const t = htmlToText(body.content, { links: true });
3557
+ return { contentType: "text", content: `${t}\r
3558
+ ` };
3559
+ }
3560
+ function bodyAsHtml(body) {
3561
+ if (body.contentType === "html") {
3562
+ return body;
3563
+ }
3564
+ const inner = body.content ? escapeHtml(body.content).replace(/\r?\n/g, "<br>\r\n") : "&nbsp;";
3565
+ return {
3566
+ contentType: "html",
3567
+ content: `<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8">\r
3568
+ <meta name="Generator" content="Microsoft Exchange Server">\r
3569
+ <!-- converted from text -->\r
3570
+ <style><!-- .EmailQuote { margin-left: 1pt; padding-left: 4pt; border-left: #800000 2px solid; } --></style></head>\r
3571
+ <body>\r
3572
+ <font size="2"><span style="font-size:11pt;"><div class="PlainText">${inner}</div></span></font>\r
3573
+ </body>\r
3574
+ </html>\r
3575
+ `
3576
+ };
3577
+ }
3578
+ function uniqueBodyAsHtml(body) {
3579
+ if (body.contentType === "html") {
3580
+ const inner2 = bodyInner(body.content).replace(/^[\r\n\t]+|[\r\n\t]+$/g, "");
3581
+ const lead = BLOCK_ELEMENT_START.test(inner2) ? "\r\n" : "";
3582
+ return { contentType: "html", content: `<html><body><div>\r
3583
+ <div>${lead}${inner2}</div></div>\r
3584
+ </body></html>` };
3585
+ }
3586
+ const inner = escapeHtml(body.content).replace(/\r?\n/g, "<br>\r\n");
3587
+ return {
3588
+ contentType: "html",
3589
+ content: `<html><body><div>\r
3590
+ <div><font size="2"><span style="font-size:11pt;">${inner}</span></font></div>\r
3591
+ </div>\r
3592
+ </body></html>`
3593
+ };
3594
+ }
3595
+ function expandTabs(text) {
3596
+ return text.split("\r\n").map((line) => {
3597
+ let out = "";
3598
+ for (const ch of line) {
3599
+ out += ch === " " ? " ".repeat(8 - out.length % 8) : ch;
3600
+ }
3601
+ return out;
3602
+ }).join("\r\n");
3603
+ }
3604
+ function bodyPreview(body) {
3605
+ if (!body.content) {
3606
+ return "";
3607
+ }
3608
+ const text = body.contentType === "html" ? expandTabs(htmlToText(body.content, { links: false })) : body.content.replace(/^\s+|\s+$/g, "");
3609
+ return text.length > PREVIEW_LIMIT ? text.slice(0, PREVIEW_LIMIT).replace(TRAILING_WHITESPACE, "") : text;
3610
+ }
3611
+ function longDate(iso, locale = "en-GB") {
3612
+ const d = new Date(iso);
3613
+ const weekday = d.toLocaleDateString(locale, { weekday: "long", timeZone: "UTC" });
3614
+ const day = d.getUTCDate();
3615
+ const month = d.toLocaleDateString(locale, { month: "long", timeZone: "UTC" });
3616
+ const time = d.toISOString().slice(11, 19);
3617
+ return `${weekday}, ${day} ${month} ${d.getUTCFullYear()} ${time}`;
3618
+ }
3619
+ function fmtAddr(a) {
3620
+ return `${escapeHtml(a.name || a.address)} &lt;${escapeHtml(a.address)}&gt;`;
3621
+ }
3622
+ function quoteBody(original, header2, comment) {
3623
+ const lines = [];
3624
+ if (header2.from) {
3625
+ lines.push(`<b>From:</b> ${fmtAddr(header2.from)}`);
3626
+ }
3627
+ lines.push(`<b>Sent:</b> ${longDate(header2.sentDateTime)}`);
3628
+ if (header2.to.length) {
3629
+ lines.push(`<b>To:</b> ${header2.to.map(fmtAddr).join("; ")}`);
3630
+ }
3631
+ if (header2.cc.length) {
3632
+ lines.push(`<b>Cc:</b> ${header2.cc.map(fmtAddr).join("; ")}`);
3633
+ }
3634
+ lines.push(`<b>Subject:</b> ${escapeHtml(header2.subject)}`);
3635
+ const lead = comment ? normalizeInnerHtml(comment) : "";
3636
+ if (original.contentType === "text") {
3637
+ const text = original.content ? escapeHtml(original.content).replace(/\r?\n/g, "<br>") : "&nbsp;";
3638
+ const inner2 = `<div>${lead}<hr tabindex="-1" style="display:inline-block; width:98%"><div id="x_divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" color="#000000" style="font-size:11pt">${lines.join("<br>")}</font><div>&nbsp;</div></div></div><font size="2"><span style="font-size:11pt"><div class="PlainText">${text}</div></span></font>`;
3639
+ return { contentType: "html", content: `${TEXT_QUOTE_HEAD}<body>${inner2}</body></html>` };
3640
+ }
3641
+ const originalInner = bodyInner(original.content).replace(TRAILING_WHITESPACE, "");
3642
+ const prefix = comment ? `${lead} ` : "";
3643
+ const inner = `${prefix}<hr tabindex="-1" style="display:inline-block; width:98%"><div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" color="#000000" style="font-size:11pt">${lines.join("<br>")}</font> <div>&nbsp;</div></div><div>${originalInner}</div>`;
3644
+ return { contentType: "html", content: `${HTML_HEAD}<body>${inner}</body></html>` };
3645
+ }
3646
+ function commentBody(comment, original) {
3647
+ const c = comment ? normalizeInnerHtml(comment) : "";
3648
+ let fragment = c ? `${c} ` : "";
3649
+ if (original.contentType === "text") {
3650
+ fragment = `<div>${c}</div>`;
3651
+ }
3652
+ return { contentType: "html", content: `<html><body>${fragment}</body></html>` };
3653
+ }
3654
+
3655
+ // src/graph/payload.ts
3656
+ var TIMEZONE_OFFSET = /[zZ]|[+-]\d{2}:\d{2}$/;
3657
+ var MILLISECONDS_FRACTION = /\.(\d{3})$/;
3658
+ var MESSAGE_WRITABLE = /* @__PURE__ */ new Set([
3659
+ "subject",
3660
+ "body",
3661
+ "toRecipients",
3662
+ "ccRecipients",
3663
+ "bccRecipients",
3664
+ "replyTo",
3665
+ "from",
3666
+ "sender",
3667
+ "importance",
3668
+ "isRead",
3669
+ "isReadReceiptRequested",
3670
+ "isDeliveryReceiptRequested",
3671
+ "categories",
3672
+ "flag",
3673
+ "inferenceClassification",
3674
+ "internetMessageId",
3675
+ "internetMessageHeaders",
3676
+ "attachments",
3677
+ "singleValueExtendedProperties",
3678
+ "multiValueExtendedProperties",
3679
+ "extensions",
3680
+ "mentions"
3681
+ ]);
3682
+ var MESSAGE_READONLY = /* @__PURE__ */ new Set([
3683
+ "id",
3684
+ "createdDateTime",
3685
+ "lastModifiedDateTime",
3686
+ "changeKey",
3687
+ "receivedDateTime",
3688
+ "sentDateTime",
3689
+ "hasAttachments",
3690
+ "bodyPreview",
3691
+ "parentFolderId",
3692
+ "conversationId",
3693
+ "conversationIndex",
3694
+ "isDraft",
3695
+ "webLink",
3696
+ "uniqueBody"
3697
+ ]);
3698
+ function unexpectedNode(prop, kind) {
3699
+ return new GraphError(
3700
+ 400,
3701
+ "RequestBodyRead",
3702
+ `An unexpected '${kind}' node was found for property named '${prop}' when reading from the JSON reader. A 'PrimitiveValue' node was expected.`
3703
+ );
3704
+ }
3705
+ function literal(v) {
3706
+ if (typeof v === "string") {
3707
+ return v;
3708
+ }
3709
+ return JSON.stringify(v);
3710
+ }
3711
+ function expectString(prop, v) {
3712
+ if (v === null) {
3713
+ return null;
3714
+ }
3715
+ if (typeof v === "string") {
3716
+ return v;
3717
+ }
3718
+ if (Array.isArray(v)) {
3719
+ throw unexpectedNode(prop, "StartArray");
3720
+ }
3721
+ if (typeof v === "object") {
3722
+ throw unexpectedNode(prop, "StartObject");
3723
+ }
3724
+ throw Errors.literalConversion(literal(v), "Edm.String");
3725
+ }
3726
+ function expectBoolean(prop, v) {
3727
+ if (typeof v === "boolean") {
3728
+ return v;
3729
+ }
3730
+ if (Array.isArray(v)) {
3731
+ throw unexpectedNode(prop, "StartArray");
3732
+ }
3733
+ if (v !== null && typeof v === "object") {
3734
+ throw unexpectedNode(prop, "StartObject");
3735
+ }
3736
+ throw Errors.literalConversion(literal(v), "Edm.Boolean");
3737
+ }
3738
+ function expectEnum(v, members) {
3739
+ if (typeof v !== "string") {
3740
+ throw Errors.enumValueNotFound(literal(v));
3741
+ }
3742
+ const m = members.find((x2) => x2.toLowerCase() === v.toLowerCase());
3743
+ if (!m) {
3744
+ throw Errors.enumValueNotFound(v);
3745
+ }
3746
+ return m;
3747
+ }
3748
+ function expectObject(_prop, v) {
3749
+ if (v === null || typeof v !== "object" || Array.isArray(v)) {
3750
+ throw new GraphError(
3751
+ 400,
3752
+ "RequestBodyRead",
3753
+ `An unexpected '${Array.isArray(v) ? "StartArray" : "PrimitiveValue"}' node was found when reading from the JSON reader. A 'StartObject' node was expected.`
3754
+ );
3755
+ }
3756
+ return v;
3757
+ }
3758
+ function expectArray(_prop, v) {
3759
+ if (v === null) {
3760
+ return [];
3761
+ }
3762
+ if (!Array.isArray(v)) {
3763
+ throw new GraphError(
3764
+ 400,
3765
+ "RequestBodyRead",
3766
+ `An unexpected '${typeof v === "object" ? "StartObject" : "PrimitiveValue"}' node was found when reading from the JSON reader. A 'StartArray' node was expected.`
3767
+ );
3768
+ }
3769
+ return v;
3770
+ }
3771
+ function actionParams(body, names, action) {
3772
+ const out = {};
3773
+ for (const [k, v] of Object.entries(body)) {
3774
+ if (k.includes("@odata.")) {
3775
+ continue;
3776
+ }
3777
+ const name = names.find((n) => n.toLowerCase() === k.toLowerCase());
3778
+ if (!name) {
3779
+ throw Errors.unknownBodyProperty(k, action);
3780
+ }
3781
+ out[name] = v;
3782
+ }
3783
+ return out;
3784
+ }
3785
+ function checkKeys(obj, allowed, type) {
3786
+ for (const k of Object.keys(obj)) {
3787
+ if (k.startsWith("@odata.") || k.includes("@odata.")) {
3788
+ continue;
3789
+ }
3790
+ if (!allowed.includes(k)) {
3791
+ throw Errors.unknownBodyProperty(k, type);
3792
+ }
3793
+ }
3794
+ }
3795
+ function parseRecipient(ex, v, prop) {
3796
+ const o = expectObject(prop, v);
3797
+ checkKeys(o, ["emailAddress"], "recipient");
3798
+ const ea = o.emailAddress === void 0 || o.emailAddress === null ? {} : expectObject("emailAddress", o.emailAddress);
3799
+ checkKeys(ea, ["address", "name"], "emailAddress");
3800
+ const address = expectString("address", ea.address ?? null);
3801
+ const name = expectString("name", ea.name ?? null);
3802
+ return ex.resolveRecipient({ emailAddress: { address: address ?? "", name } });
3803
+ }
3804
+ function parseRecipients(ex, v, prop) {
3805
+ return expectArray(prop, v).map((r) => parseRecipient(ex, r, prop));
3806
+ }
3807
+ function parseBody(v) {
3808
+ if (v === null) {
3809
+ return { contentType: "text", content: "" };
3810
+ }
3811
+ const o = expectObject("body", v);
3812
+ checkKeys(o, ["contentType", "content"], "itemBody");
3813
+ const contentType = o.contentType === void 0 || o.contentType === null ? "text" : expectEnum(o.contentType, ["text", "html"]);
3814
+ const content = expectString("content", o.content ?? "") ?? "";
3815
+ return storeBody({ contentType, content });
3816
+ }
3817
+ function parseDateTimeTimeZone(v, prop) {
3818
+ if (v === null || v === void 0) {
3819
+ return void 0;
3820
+ }
3821
+ const o = expectObject(prop, v);
3822
+ checkKeys(o, ["dateTime", "timeZone"], "dateTimeTimeZone");
3823
+ const dt = expectString("dateTime", o.dateTime ?? null);
3824
+ if (!dt) {
3825
+ throw Errors.requiredProperty("dateTime");
3826
+ }
3827
+ const d = new Date(TIMEZONE_OFFSET.test(dt) ? dt : `${dt}Z`);
3828
+ const iso = Number.isNaN(d.getTime()) ? dt : d.toISOString().replace("Z", "").replace(MILLISECONDS_FRACTION, ".$10000");
3829
+ return { dateTime: iso, timeZone: o.timeZone ?? "UTC" };
3830
+ }
3831
+ function parseFlag(v) {
3832
+ const o = expectObject("flag", v);
3833
+ checkKeys(o, ["flagStatus", "startDateTime", "dueDateTime", "completedDateTime"], "followupFlag");
3834
+ const status = o.flagStatus === void 0 ? "notFlagged" : expectEnum(o.flagStatus, ["notFlagged", "complete", "flagged"]);
3835
+ const flag = { flagStatus: status };
3836
+ const start = parseDateTimeTimeZone(o.startDateTime, "startDateTime");
3837
+ const due = parseDateTimeTimeZone(o.dueDateTime, "dueDateTime");
3838
+ const completed = parseDateTimeTimeZone(o.completedDateTime, "completedDateTime");
3839
+ if (status === "flagged") {
3840
+ if (start) {
3841
+ flag.startDateTime = start;
3842
+ }
3843
+ if (due) {
3844
+ flag.dueDateTime = due;
3845
+ }
3846
+ }
3847
+ if (status === "complete" && completed) {
3848
+ flag.completedDateTime = {
3849
+ dateTime: `${completed.dateTime.slice(0, 10)}T00:00:00.0000000`,
3850
+ timeZone: completed.timeZone
3851
+ };
3852
+ }
3853
+ return flag;
3854
+ }
3855
+ function parseMessageInput(ex, input, mode) {
3856
+ const fields = {};
3857
+ const present = /* @__PURE__ */ new Set();
3858
+ let attachments = null;
3859
+ for (const [k, v] of Object.entries(input)) {
3860
+ if (k.startsWith("@odata.") || k.includes("@odata.")) {
3861
+ continue;
3862
+ }
3863
+ if (MESSAGE_READONLY.has(k)) {
3864
+ continue;
3865
+ }
3866
+ if (!MESSAGE_WRITABLE.has(k)) {
3867
+ throw Errors.unknownBodyProperty(k, "message");
3868
+ }
3869
+ switch (k) {
3870
+ case "subject":
3871
+ fields.subject = expectString(k, v) ?? "";
3872
+ break;
3873
+ case "body":
3874
+ fields.body = parseBody(v);
3875
+ break;
3876
+ case "toRecipients":
3877
+ case "ccRecipients":
3878
+ case "bccRecipients":
3879
+ case "replyTo":
3880
+ fields[k] = parseRecipients(ex, v, k);
3881
+ break;
3882
+ case "from":
3883
+ case "sender":
3884
+ fields[k] = v === null ? null : parseRecipient(ex, v, k);
3885
+ break;
3886
+ case "importance":
3887
+ fields.importance = expectEnum(v, ["low", "normal", "high"]);
3888
+ break;
3889
+ case "inferenceClassification":
3890
+ fields.inferenceClassification = expectEnum(v, ["focused", "other"]);
3891
+ break;
3892
+ case "isRead":
3893
+ case "isReadReceiptRequested":
3894
+ case "isDeliveryReceiptRequested":
3895
+ fields[k] = expectBoolean(k, v);
3896
+ break;
3897
+ case "categories":
3898
+ fields.categories = expectArray(k, v).map((c) => expectString(k, c) ?? "");
3899
+ break;
3900
+ case "flag":
3901
+ fields.flag = parseFlag(v);
3902
+ break;
3903
+ case "internetMessageId":
3904
+ if (mode !== "create") {
3905
+ continue;
3906
+ }
3907
+ fields.internetMessageId = expectString(k, v) ?? "";
3908
+ break;
3909
+ case "internetMessageHeaders": {
3910
+ if (mode === "update") {
3911
+ continue;
3912
+ }
3913
+ const list = expectArray(k, v).map((h) => {
3914
+ const o = expectObject(k, h);
3915
+ checkKeys(o, ["name", "value"], "internetMessageHeader");
3916
+ return { name: String(o.name ?? ""), value: String(o.value ?? "") };
3917
+ });
3918
+ fields.internetMessageHeaders = list;
3919
+ break;
3920
+ }
3921
+ case "attachments":
3922
+ attachments = expectArray(k, v);
3923
+ break;
3924
+ case "singleValueExtendedProperties":
3925
+ fields.singleValueExtendedProperties = expectArray(k, v).map((p) => {
3926
+ const o = expectObject(k, p);
3927
+ return { id: String(o.id ?? ""), value: String(o.value ?? "") };
3928
+ });
3929
+ break;
3930
+ case "multiValueExtendedProperties":
3931
+ fields.multiValueExtendedProperties = expectArray(k, v).map((p) => {
3932
+ const o = expectObject(k, p);
3933
+ return { id: String(o.id ?? ""), value: expectArray("value", o.value).map(String) };
3934
+ });
3935
+ break;
3936
+ case "extensions":
3937
+ fields.extensions = expectArray(k, v).map((e) => expectObject(k, e));
3938
+ break;
3939
+ case "mentions":
3940
+ break;
3941
+ default:
3942
+ break;
3943
+ }
3944
+ present.add(k);
3945
+ }
3946
+ return { fields, present, attachments };
3947
+ }
3948
+
3949
+ // src/graph/serialize.ts
3950
+ import { createHash } from "crypto";
3951
+ var COLLECTION_TYPE = /^Collection\((.*)\)$/;
3952
+ var GRAPH_TYPE_CAST_PREFIX = /^microsoft\.graph\.\w+\//;
3953
+ var MESSAGE_PROPERTIES = [
3954
+ { name: "createdDateTime", type: "Edm.DateTimeOffset", default: true },
3955
+ { name: "lastModifiedDateTime", type: "Edm.DateTimeOffset", default: true },
3956
+ { name: "changeKey", type: "Edm.String", default: true },
3957
+ { name: "categories", type: "Collection(Edm.String)", default: true, freeFilter: true },
3958
+ { name: "receivedDateTime", type: "Edm.DateTimeOffset", default: true },
3959
+ { name: "sentDateTime", type: "Edm.DateTimeOffset", default: true },
3960
+ { name: "hasAttachments", type: "Edm.Boolean", default: true },
3961
+ { name: "internetMessageId", type: "Edm.String", default: true },
3962
+ { name: "subject", type: "Edm.String", default: true },
3963
+ { name: "bodyPreview", type: "Edm.String", default: true },
3964
+ { name: "importance", type: "importance", default: true },
3965
+ { name: "parentFolderId", type: "Edm.String", default: true },
3966
+ { name: "conversationId", type: "Edm.String", default: true },
3967
+ { name: "conversationIndex", type: "Edm.Binary", default: true },
3968
+ { name: "isDeliveryReceiptRequested", type: "Edm.Boolean", default: true },
3969
+ { name: "isReadReceiptRequested", type: "Edm.Boolean", default: true },
3970
+ { name: "isRead", type: "Edm.Boolean", default: true, freeFilter: true, unsortable: true },
3971
+ { name: "isDraft", type: "Edm.Boolean", default: true, freeFilter: true },
3972
+ { name: "webLink", type: "Edm.String", default: true },
3973
+ { name: "inferenceClassification", type: "inferenceClassificationType", default: true },
3974
+ { name: "body", type: "itemBody", default: true },
3975
+ { name: "sender", type: "recipient", default: true },
3976
+ { name: "from", type: "recipient", default: true },
3977
+ { name: "toRecipients", type: "Collection(recipient)", default: true },
3978
+ { name: "ccRecipients", type: "Collection(recipient)", default: true },
3979
+ { name: "bccRecipients", type: "Collection(recipient)", default: true },
3980
+ { name: "replyTo", type: "Collection(recipient)", default: true },
3981
+ { name: "flag", type: "followupFlag", default: true },
3982
+ { name: "uniqueBody", type: "itemBody", default: false },
3983
+ { name: "internetMessageHeaders", type: "Collection(internetMessageHeader)", default: false }
3984
+ ];
3985
+ var MESSAGE_NAVIGATION = [
3986
+ "attachments",
3987
+ "extensions",
3988
+ "singleValueExtendedProperties",
3989
+ "multiValueExtendedProperties"
3990
+ ];
3991
+ var COMPLEX_TYPES = {
3992
+ itemBody: { contentType: "bodyType", content: "Edm.String" },
3993
+ recipient: { emailAddress: "emailAddress" },
3994
+ emailAddress: { name: "Edm.String", address: "Edm.String" },
3995
+ followupFlag: {
3996
+ flagStatus: "followupFlagStatus",
3997
+ startDateTime: "dateTimeTimeZone",
3998
+ dueDateTime: "dateTimeTimeZone",
3999
+ completedDateTime: "dateTimeTimeZone"
4000
+ },
4001
+ dateTimeTimeZone: { dateTime: "Edm.String", timeZone: "Edm.String" },
4002
+ internetMessageHeader: { name: "Edm.String", value: "Edm.String" }
4003
+ };
4004
+ function messagePathType(path) {
4005
+ if (path[0] === "id") {
4006
+ return "Edm.String";
4007
+ }
4008
+ const p = MESSAGE_PROPERTIES.find((x2) => x2.name.toLowerCase() === path[0].toLowerCase());
4009
+ if (!p) {
4010
+ return null;
4011
+ }
4012
+ let t = p.type;
4013
+ for (const seg of path.slice(1)) {
4014
+ const inner = COLLECTION_TYPE.exec(t)?.[1] ?? t;
4015
+ const c = COMPLEX_TYPES[inner];
4016
+ if (!c) {
4017
+ return null;
4018
+ }
4019
+ const key = Object.keys(c).find((k) => k.toLowerCase() === seg.toLowerCase());
4020
+ if (!key) {
4021
+ return null;
4022
+ }
4023
+ t = c[key];
4024
+ }
4025
+ return t;
4026
+ }
4027
+ var IMPORTANCE_RANK = { low: 0, normal: 1, high: 2 };
4028
+ function importanceRank(v) {
4029
+ return typeof v === "string" && v in IMPORTANCE_RANK ? IMPORTANCE_RANK[v] : v;
4030
+ }
4031
+ function messageValue(ex, mbx, m, path) {
4032
+ const head = path[0].toLowerCase();
4033
+ let v;
4034
+ switch (head) {
4035
+ case "id":
4036
+ return ex.itemId(mbx, m);
4037
+ case "hasattachments":
4038
+ return m.attachments.some((a) => !a.isInline);
4039
+ case "isdraft":
4040
+ return m.state === "draft";
4041
+ case "bodypreview":
4042
+ return bodyPreview(m.body);
4043
+ case "parentfolderid":
4044
+ return ex.folderId(mbx, m.folderCounter);
4045
+ case "conversationid":
4046
+ return ex.conversationId(mbx, m);
4047
+ case "changekey":
4048
+ return ex.changeKey(mbx, m);
4049
+ default: {
4050
+ const key = Object.keys(m).find((k) => k.toLowerCase() === head);
4051
+ v = key ? m[key] : void 0;
4052
+ }
4053
+ }
4054
+ for (const seg of path.slice(1)) {
4055
+ if (v === null || v === void 0) {
4056
+ return null;
4057
+ }
4058
+ if (typeof v === "object") {
4059
+ const obj = v;
4060
+ const key = Object.keys(obj).find((k) => k.toLowerCase() === seg.toLowerCase());
4061
+ v = key ? obj[key] : void 0;
4062
+ }
4063
+ }
4064
+ return v ?? null;
4065
+ }
4066
+ function flagJson(m) {
4067
+ const f = { flagStatus: m.flag.flagStatus };
4068
+ if (m.flag.flagStatus === "flagged") {
4069
+ if (m.flag.dueDateTime) {
4070
+ f.dueDateTime = m.flag.dueDateTime;
4071
+ }
4072
+ if (m.flag.startDateTime) {
4073
+ f.startDateTime = m.flag.startDateTime;
4074
+ }
4075
+ }
4076
+ if (m.flag.flagStatus === "complete" && m.flag.completedDateTime) {
4077
+ f.completedDateTime = m.flag.completedDateTime;
4078
+ }
4079
+ return f;
4080
+ }
4081
+ function webLink(ex, mbx, m, immutable = false) {
4082
+ const std = Buffer.from(ex.itemId(mbx, m, immutable).replace(/-/g, "/").replace(/_/g, "+"), "base64").toString(
4083
+ "base64"
4084
+ );
4085
+ return `https://outlook.office365.com/owa/?ItemID=${encodeURIComponent(std)}&exvsurl=1&viewmodel=ReadMessageItem`;
4086
+ }
4087
+ function propertyValue(ex, mbx, m, name, opts) {
4088
+ switch (name) {
4089
+ case "createdDateTime":
4090
+ return m.createdDateTime;
4091
+ case "lastModifiedDateTime":
4092
+ return m.lastModifiedDateTime;
4093
+ case "changeKey":
4094
+ return ex.changeKey(mbx, m);
4095
+ case "categories":
4096
+ return [...m.categories];
4097
+ case "receivedDateTime":
4098
+ return m.receivedDateTime;
4099
+ case "sentDateTime":
4100
+ return m.sentDateTime;
4101
+ case "hasAttachments":
4102
+ return m.attachments.some((a) => !a.isInline);
4103
+ case "internetMessageId":
4104
+ return m.internetMessageId;
4105
+ case "subject":
4106
+ return m.subject;
4107
+ case "bodyPreview":
4108
+ return bodyPreview(m.body);
4109
+ case "importance":
4110
+ return m.importance;
4111
+ case "parentFolderId":
4112
+ return ex.folderId(mbx, m.folderCounter);
4113
+ case "conversationId":
4114
+ return ex.conversationId(mbx, m);
4115
+ case "conversationIndex":
4116
+ return m.conversationIndex;
4117
+ case "isDeliveryReceiptRequested":
4118
+ return m.isDeliveryReceiptRequested;
4119
+ case "isReadReceiptRequested":
4120
+ return m.isReadReceiptRequested;
4121
+ case "isRead":
4122
+ return m.isRead;
4123
+ case "isDraft":
4124
+ return m.state === "draft" || m.state === "submitted";
4125
+ case "webLink":
4126
+ return webLink(ex, mbx, m, opts.immutableIds);
4127
+ case "inferenceClassification":
4128
+ return m.inferenceClassification;
4129
+ case "body":
4130
+ if (opts.bodyType === "text" && m.body.contentType === "html") {
4131
+ return m.textBody === void 0 ? bodyAsText(m.body) : { contentType: "text", content: m.textBody };
4132
+ }
4133
+ if (opts.bodyType === "html") {
4134
+ return bodyAsHtml(m.body);
4135
+ }
4136
+ return { ...m.body };
4137
+ case "uniqueBody": {
4138
+ const u = m.uniqueBody ?? m.body;
4139
+ if (opts.bodyType === "html") {
4140
+ return uniqueBodyAsHtml(u);
4141
+ }
4142
+ const draft = m.state === "draft" || m.state === "submitted";
4143
+ if (opts.bodyType !== "text" && !draft && u.contentType === "html") {
4144
+ return uniqueBodyAsHtml(u);
4145
+ }
4146
+ return bodyAsText(u);
4147
+ }
4148
+ case "sender":
4149
+ return m.sender;
4150
+ case "from":
4151
+ return m.from;
4152
+ case "toRecipients":
4153
+ return m.toRecipients;
4154
+ case "ccRecipients":
4155
+ return m.ccRecipients;
4156
+ case "bccRecipients":
4157
+ return m.bccRecipients;
4158
+ case "replyTo":
4159
+ return m.replyTo;
4160
+ case "flag":
4161
+ return flagJson(m);
4162
+ case "internetMessageHeaders":
4163
+ return m.internetMessageHeaders;
4164
+ default:
4165
+ break;
4166
+ }
4167
+ return void 0;
4168
+ }
4169
+ function canonicalMessageSelect(select) {
4170
+ const names = [];
4171
+ const nav = [];
4172
+ for (const s of select) {
4173
+ if (s === "*") {
4174
+ names.push(...MESSAGE_PROPERTIES.filter((property) => property.default).map((property) => property.name));
4175
+ continue;
4176
+ }
4177
+ const raw = s.replace(GRAPH_TYPE_CAST_PREFIX, "");
4178
+ if (raw.toLowerCase() === "id") {
4179
+ names.push("id");
4180
+ continue;
4181
+ }
4182
+ const p = MESSAGE_PROPERTIES.find((x2) => x2.name.toLowerCase() === raw.toLowerCase());
4183
+ if (p) {
4184
+ names.push(p.name);
4185
+ continue;
4186
+ }
4187
+ const n = MESSAGE_NAVIGATION.find((x2) => x2.toLowerCase() === raw.toLowerCase());
4188
+ if (n) {
4189
+ nav.push(n);
4190
+ continue;
4191
+ }
4192
+ throw Errors.parseUriProperty(raw, "Microsoft.OutlookServices.Message", true);
4193
+ }
4194
+ return { names: [...new Set(names)], nav: [...new Set(nav)] };
4195
+ }
4196
+ function renderMessage(ex, mbx, m, opts) {
4197
+ const out = {};
4198
+ out["@odata.etag"] = `W/"${ex.changeKey(mbx, m)}"`;
4199
+ out.id = ex.itemId(mbx, m, opts.immutableIds);
4200
+ const selected = opts.select ? new Set(opts.select) : null;
4201
+ const isDraftLike = m.state === "draft";
4202
+ for (const p of MESSAGE_PROPERTIES) {
4203
+ if (selected ? !selected.has(p.name) : !p.default) {
4204
+ continue;
4205
+ }
4206
+ if ((p.name === "sender" || p.name === "from") && !selected && isDraftLike && !m[p.name]) {
4207
+ continue;
4208
+ }
4209
+ if ((p.name === "sender" || p.name === "from") && selected && !m[p.name]) {
4210
+ continue;
4211
+ }
4212
+ if (p.name === "internetMessageHeaders" && !m.internetMessageHeaders) {
4213
+ continue;
4214
+ }
4215
+ if (p.name === "webLink" && selected === null) {
4216
+ out.webLink = webLink(ex, mbx, m, opts.immutableIds);
4217
+ continue;
4218
+ }
4219
+ let v = propertyValue(ex, mbx, m, p.name, opts);
4220
+ if (p.name === "parentFolderId") {
4221
+ v = ex.folderId(mbx, m.folderCounter);
4222
+ }
4223
+ out[p.name] = v;
4224
+ }
4225
+ if (opts.expandAttachments) {
4226
+ out.attachments = m.attachments.map((a) => renderAttachment(ex, mbx, m, a, true));
4227
+ }
4228
+ if (m.sharing) {
4229
+ return withSharingProperties(out, m.sharing, selected === null);
4230
+ }
4231
+ return m.meeting ? withMeetingProperties(out, m.meeting, selected === null) : out;
4232
+ }
4233
+ function withSharingProperties(base, info, full) {
4234
+ const out = { "@odata.type": "#microsoft.graph.calendarSharingMessage" };
4235
+ if (!full) {
4236
+ return Object.assign(out, base);
4237
+ }
4238
+ const action = { importance: "primary", actionType: "accept", action: "acceptAndViewCalendar" };
4239
+ for (const [k, v] of Object.entries(base)) {
4240
+ out[k] = v;
4241
+ if (k === "inferenceClassification") {
4242
+ out.canAccept = true;
4243
+ out.suggestedCalendarName = info.suggestedCalendarName;
4244
+ }
4245
+ }
4246
+ out.sharingMessageAction = action;
4247
+ out.sharingMessageActions = [action];
4248
+ return out;
4249
+ }
4250
+ var MEETING_TYPE = {
4251
+ request: "#microsoft.graph.eventMessageRequest",
4252
+ response: "#microsoft.graph.eventMessageResponse",
4253
+ cancellation: "#microsoft.graph.eventMessage"
4254
+ };
4255
+ function utcDtz(ms) {
4256
+ const iso = new Date(ms).toISOString();
4257
+ return { dateTime: `${iso.slice(0, 19)}.${iso.slice(20, 23)}0000`, timeZone: "UTC" };
4258
+ }
4259
+ function withMeetingProperties(base, info, full) {
4260
+ const out = { "@odata.type": MEETING_TYPE[info.kind] };
4261
+ if (!full) {
4262
+ return Object.assign(out, base);
4263
+ }
4264
+ const scalars = {
4265
+ meetingMessageType: info.meetingMessageType,
4266
+ type: info.type,
4267
+ isOutOfDate: false,
4268
+ isAllDay: info.isAllDay,
4269
+ isDelegated: false
4270
+ };
4271
+ if (info.kind === "request") {
4272
+ scalars.responseRequested = info.responseRequested;
4273
+ scalars.allowNewTimeProposals = null;
4274
+ scalars.meetingRequestType = info.meetingRequestType ?? "newMeetingRequest";
4275
+ }
4276
+ if (info.kind === "response") {
4277
+ scalars.responseType = info.responseType ?? "none";
4278
+ }
4279
+ scalars.recurrence = info.recurrence;
4280
+ const tail = {
4281
+ startDateTime: utcDtz(info.startMs),
4282
+ endDateTime: utcDtz(info.endMs)
4283
+ };
4284
+ const located = Boolean(info.location.displayName);
4285
+ if (located) {
4286
+ tail.location = { ...info.location };
4287
+ }
4288
+ if (info.kind === "request") {
4289
+ tail.previousLocation = info.previous ? { ...info.previous.location } : null;
4290
+ tail.previousStartDateTime = utcDtz(info.previous ? info.previous.startMs : info.startMs);
4291
+ tail.previousEndDateTime = utcDtz(info.previous ? info.previous.endMs : info.endMs);
4292
+ }
4293
+ if (info.kind === "response" && info.proposedNewTime) {
4294
+ tail.proposedNewTime = info.proposedNewTime ? { start: utcDtz(info.proposedNewTime.startMs), end: utcDtz(info.proposedNewTime.endMs) } : null;
4295
+ }
4296
+ for (const [k, v] of Object.entries(base)) {
4297
+ if (k === "body") {
4298
+ Object.assign(out, scalars);
4299
+ }
4300
+ out[k] = v;
4301
+ if (k === "flag") {
4302
+ Object.assign(out, tail);
4303
+ }
4304
+ }
4305
+ if (!("body" in base)) {
4306
+ Object.assign(out, scalars);
4307
+ }
4308
+ if (!("flag" in base)) {
4309
+ Object.assign(out, tail);
4310
+ }
4311
+ return out;
4312
+ }
4313
+ var FOLDER_PROPERTIES = [
4314
+ "displayName",
4315
+ "parentFolderId",
4316
+ "childFolderCount",
4317
+ "unreadItemCount",
4318
+ "totalItemCount",
4319
+ "sizeInBytes",
4320
+ "wellKnownName",
4321
+ "isHidden"
4322
+ ];
4323
+ var FOLDER_SELECTABLE = [
4324
+ "id",
4325
+ "displayName",
4326
+ "parentFolderId",
4327
+ "childFolderCount",
4328
+ "unreadItemCount",
4329
+ "totalItemCount",
4330
+ "isHidden"
4331
+ ];
4332
+ function renderFolder(ex, mbx, f, select) {
4333
+ const counts = ex.counts(mbx, f);
4334
+ const all = {
4335
+ displayName: f.displayName,
4336
+ parentFolderId: f.parentCounter === null ? null : ex.folderId(mbx, f.parentCounter),
4337
+ childFolderCount: counts.childFolderCount,
4338
+ unreadItemCount: counts.unreadItemCount,
4339
+ totalItemCount: counts.totalItemCount,
4340
+ sizeInBytes: counts.sizeInBytes,
4341
+ wellKnownName: f.wellKnownName,
4342
+ isHidden: f.isHidden
4343
+ };
4344
+ const out = f.search ? { "@odata.type": "#microsoft.graph.mailSearchFolder", id: ex.folderId(mbx, f) } : { id: ex.folderId(mbx, f) };
4345
+ for (const k of FOLDER_PROPERTIES) {
4346
+ if (select && !select.includes(k)) {
4347
+ continue;
4348
+ }
4349
+ out[k] = all[k];
4350
+ }
4351
+ if (f.search) {
4352
+ const search = f.search.unsupported ? { isSupported: false } : {
4353
+ isSupported: true,
4354
+ includeNestedFolders: f.search.includeNestedFolders,
4355
+ sourceFolderIds: f.search.sourceFolderCounters.map((c) => ex.folderId(mbx, c)),
4356
+ filterQuery: f.search.filterQuery
4357
+ };
4358
+ for (const [k, v] of Object.entries(search)) {
4359
+ if (select && !select.some((x2) => x2.toLowerCase() === k.toLowerCase())) {
4360
+ continue;
4361
+ }
4362
+ out[k] = v;
4363
+ }
4364
+ }
4365
+ return out;
4366
+ }
4367
+ function canonicalFolderSelect(select) {
4368
+ const out = [];
4369
+ for (const s of select) {
4370
+ const k = FOLDER_SELECTABLE.find((x2) => x2.toLowerCase() === s.toLowerCase());
4371
+ if (!k) {
4372
+ const nav = [
4373
+ "childFolders",
4374
+ "messages",
4375
+ "messageRules",
4376
+ "singleValueExtendedProperties",
4377
+ "multiValueExtendedProperties"
4378
+ ].find((x2) => x2.toLowerCase() === s.toLowerCase());
4379
+ if (nav) {
4380
+ continue;
4381
+ }
4382
+ throw Errors.selectExpandParse(s, "mailFolder");
4383
+ }
4384
+ out.push(k);
4385
+ }
4386
+ return [...new Set(out)];
4387
+ }
4388
+ function folderValue(ex, mbx, f, path) {
4389
+ const r = renderFolder(ex, mbx, f, null);
4390
+ const key = Object.keys(r).find((k) => k.toLowerCase() === path[0].toLowerCase());
4391
+ return key ? r[key] : void 0;
4392
+ }
4393
+ function folderPathType(path) {
4394
+ const types = {
4395
+ id: "Edm.String",
4396
+ displayName: "Edm.String",
4397
+ parentFolderId: "Edm.String",
4398
+ childFolderCount: "Edm.Int32",
4399
+ unreadItemCount: "Edm.Int32",
4400
+ totalItemCount: "Edm.Int32",
4401
+ isHidden: "Edm.Boolean"
4402
+ };
4403
+ if (path.length !== 1) {
4404
+ return null;
4405
+ }
4406
+ const k = Object.keys(types).find((x2) => x2.toLowerCase() === path[0].toLowerCase());
4407
+ return k ? types[k] : null;
4408
+ }
4409
+ function attachmentId(ex, mbx, m, a) {
4410
+ const item = Buffer.from(ex.itemId(mbx, m).replace(/-/g, "/").replace(/_/g, "+"), "base64");
4411
+ const key = createHash("sha1").update(`${mbx.guid}:${a.counter}`).digest().subarray(0, 16);
4412
+ key.writeUInt32BE(a.counter >>> 0, 12);
4413
+ return Buffer.concat([item, Buffer.from([1, 18, 0, 16, 0]), key]).toString("base64").replace(/\//g, "-").replace(/\+/g, "_");
4414
+ }
4415
+ function decodeAttachmentId(id) {
4416
+ const buf = Buffer.from(id.replace(/-/g, "/").replace(/_/g, "+"), "base64");
4417
+ const n = buf.length - 21;
4418
+ if (n !== 111 && n !== 113) {
4419
+ return null;
4420
+ }
4421
+ if (buf[n] !== 1 || buf[n + 1] !== 18 || buf[n + 3] !== 16) {
4422
+ return null;
4423
+ }
4424
+ const itemId = buf.subarray(0, n).toString("base64").replace(/\//g, "-").replace(/\+/g, "_");
4425
+ return { itemId, counter: buf.readUInt32BE(n + 17) };
4426
+ }
4427
+ function renderAttachment(ex, mbx, m, a, withContent, select = null) {
4428
+ const id = attachmentId(ex, mbx, m, a);
4429
+ let out;
4430
+ if (a.type === "file") {
4431
+ out = {
4432
+ "@odata.type": "#microsoft.graph.fileAttachment",
4433
+ "@odata.mediaContentType": a.contentType,
4434
+ id,
4435
+ lastModifiedDateTime: a.lastModifiedDateTime,
4436
+ name: a.name,
4437
+ contentType: a.contentType,
4438
+ size: a.size,
4439
+ isInline: a.isInline,
4440
+ contentId: a.contentId,
4441
+ contentLocation: a.contentLocation
4442
+ };
4443
+ if (withContent) {
4444
+ out.contentBytes = a.contentBytes;
4445
+ }
4446
+ } else if (a.type === "item") {
4447
+ out = {
4448
+ "@odata.type": "#microsoft.graph.itemAttachment",
4449
+ id,
4450
+ lastModifiedDateTime: a.lastModifiedDateTime,
4451
+ name: a.name,
4452
+ contentType: null,
4453
+ size: a.size,
4454
+ isInline: a.isInline
4455
+ };
4456
+ } else {
4457
+ out = {
4458
+ "@odata.type": "#microsoft.graph.referenceAttachment",
4459
+ id,
4460
+ lastModifiedDateTime: a.lastModifiedDateTime,
4461
+ name: a.name,
4462
+ contentType: a.contentType,
4463
+ size: a.size,
4464
+ isInline: a.isInline
4465
+ };
4466
+ }
4467
+ if (select) {
4468
+ const keep = /* @__PURE__ */ new Set(["@odata.type", "@odata.mediaContentType", "id", ...select]);
4469
+ for (const k of Object.keys(out)) {
4470
+ if (!keep.has(k)) {
4471
+ delete out[k];
4472
+ }
4473
+ }
4474
+ }
4475
+ return out;
4476
+ }
4477
+
4478
+ // src/graph/handlers/upload.ts
4479
+ var MILLISECONDS_SUFFIX_GROUP = /\.(\d{3})Z$/;
4480
+ var CONTENT_RANGE = /^bytes (\d+)-(\d+)\/(\d+|\*)$/;
4481
+ var MAX_SIZE = 150 * 1024 * 1024;
4482
+ var SESSION_TTL_MS = 2 * 60 * 60 * 1e3;
4483
+ function ticksIso(d) {
4484
+ return d.toISOString().replace(
4485
+ MILLISECONDS_SUFFIX_GROUP,
4486
+ (_m, ms) => `.${ms}${String(Math.floor(Math.random() * 1e4)).padStart(4, "0")}Z`
4487
+ );
4488
+ }
4489
+ function sessionId() {
4490
+ return Buffer.concat([Buffer.from([0, 0]), randomBytes4(68)]).toString("base64").replace(/\//g, "-").replace(/\+/g, "_");
4491
+ }
4492
+ function createUploadSession(c, m, collection = "messages", itemKey) {
4493
+ const body = parseJsonBody(c.req);
4494
+ const key = Object.keys(body).find((k) => k.toLowerCase() === "attachmentitem");
4495
+ if (!key || body[key] === null || body[key] === void 0) {
4496
+ throw Errors.missingParameters("createUploadSession", ["AttachmentItem"]);
4497
+ }
4498
+ const item = body[key];
4499
+ if (typeof item !== "object" || Array.isArray(item)) {
4500
+ throw Errors.unreadableJson();
4501
+ }
4502
+ for (const k of Object.keys(item)) {
4503
+ if (!(["attachmentType", "name", "size", "isInline", "contentType", "contentId"].includes(k) || k.startsWith("@odata"))) {
4504
+ throw Errors.unknownBodyProperty(k, "attachmentItem");
4505
+ }
4506
+ }
4507
+ const type = item.attachmentType === void 0 ? "file" : expectEnum(item.attachmentType, ["file", "item", "reference"]);
4508
+ if (type !== "file") {
4509
+ throw Errors.notImplemented(`upload sessions for ${type} attachments`);
4510
+ }
4511
+ const size = typeof item.size === "number" ? item.size : 0;
4512
+ if (size <= 0) {
4513
+ throw Errors.attachmentTooSmall();
4514
+ }
4515
+ if (size > MAX_SIZE) {
4516
+ throw Errors.attachmentTooLarge();
4517
+ }
4518
+ const name = expectString("name", item.name ?? null) ?? "";
4519
+ const id = sessionId();
4520
+ const authtoken = randomBytes4(64).toString("base64url");
4521
+ const expires = new Date(Date.now() + SESSION_TTL_MS);
4522
+ const rec = {
4523
+ id,
4524
+ mailboxGuid: c.mbx.guid,
4525
+ itemCounter: m.immutableCounter,
4526
+ name,
4527
+ contentType: item.contentType === void 0 || item.contentType === null ? "application/octet-stream" : expectString("contentType", item.contentType) ?? "application/octet-stream",
4528
+ isInline: item.isInline === void 0 ? false : expectBoolean("isInline", item.isInline),
4529
+ contentId: item.contentId ? expectString("contentId", item.contentId) : null,
4530
+ size,
4531
+ received: 0,
4532
+ chunks: [],
4533
+ expirationDateTime: ticksIso(expires),
4534
+ authtoken,
4535
+ completed: false,
4536
+ collection
4537
+ };
4538
+ c.ex.state.uploadSessions[id] = rec;
4539
+ const tenant = c.ex.state.tenantId;
4540
+ const msgId = itemKey ?? c.ex.itemId(c.mbx, m);
4541
+ return {
4542
+ status: 201,
4543
+ headers: { "Content-Type": ODATA_JSON },
4544
+ body: {
4545
+ "@odata.context": `${c.base}/$metadata#microsoft.graph.uploadSession`,
4546
+ uploadUrl: `${c.svc.baseUrl}/api/gv1.0/users('${c.mbx.guid}@${tenant}')/${collection}('${msgId}')/AttachmentSessions('${id}')?authtoken=${authtoken}`,
4547
+ expirationDateTime: rec.expirationDateTime,
4548
+ nextExpectedRanges: ["0-"]
4549
+ }
4550
+ };
4551
+ }
4552
+ function outlookError(err, requestId) {
4553
+ const r = renderError(new GraphError(err.status, err.code, err.message, "exchange"), {
4554
+ requestId,
4555
+ clientRequestId: requestId
4556
+ });
4557
+ return {
4558
+ status: r.status,
4559
+ headers: { "Content-Type": OUTLOOK_JSON, "OData-Version": "4.0", "request-id": requestId },
4560
+ body: r.body
4561
+ };
4562
+ }
4563
+ function cancelUploadSession(ex, params) {
4564
+ const requestId = randomUUID3();
4565
+ const rec = ex.state.uploadSessions[params.session];
4566
+ if (!rec || rec.completed || params.authtoken !== rec.authtoken || Date.parse(rec.expirationDateTime) < Date.now()) {
4567
+ return outlookError(Errors.attachmentNotFound(), requestId);
4568
+ }
4569
+ delete ex.state.uploadSessions[params.session];
4570
+ return { status: 204, headers: { "request-id": requestId }, body: null };
4571
+ }
4572
+ function uploadChunk(ex, baseUrl, params) {
4573
+ const requestId = randomUUID3();
4574
+ const rec = ex.state.uploadSessions[params.session];
4575
+ const mbx = rec ? ex.mailboxByGuid(rec.mailboxGuid) : void 0;
4576
+ if (!rec || rec.completed || !mbx || params.authtoken !== rec.authtoken || Date.parse(rec.expirationDateTime) < Date.now()) {
4577
+ return outlookError(Errors.attachmentNotFound(), requestId);
4578
+ }
4579
+ const range = CONTENT_RANGE.exec((params.contentRange ?? "").trim());
4580
+ if (!range) {
4581
+ return outlookError(Errors.uploadInvalidRange(), requestId);
4582
+ }
4583
+ const start = Number(range[1]);
4584
+ const end = Number(range[2]);
4585
+ if (start !== rec.received) {
4586
+ return outlookError(Errors.uploadInvalidStart(), requestId);
4587
+ }
4588
+ if (end < start || end - start + 1 !== params.bytes.length || end >= rec.size) {
4589
+ return outlookError(Errors.uploadInvalidRange(), requestId);
4590
+ }
4591
+ rec.chunks.push(params.bytes.toString("base64"));
4592
+ rec.received = end + 1;
4593
+ const counter = mbx.immutable[rec.itemCounter];
4594
+ const isEvent = rec.collection === "events";
4595
+ let item;
4596
+ if (counter !== void 0 && counter >= 0) {
4597
+ item = isEvent ? mbx.events?.[counter] : mbx.items[counter];
4598
+ }
4599
+ if (!item) {
4600
+ return outlookError(Errors.attachmentNotFound(), requestId);
4601
+ }
4602
+ const collection = isEvent ? "events" : "messages";
4603
+ const msgId = isEvent ? eventId(mbx, item) : ex.itemId(mbx, item);
4604
+ const userKey = `${mbx.guid}@${ex.state.tenantId}`;
4605
+ if (rec.received < rec.size) {
4606
+ return {
4607
+ status: 200,
4608
+ headers: { "Content-Type": OUTLOOK_JSON, "OData-Version": "4.0", "request-id": requestId },
4609
+ body: {
4610
+ "@odata.context": `${baseUrl}/api/gv1.0/$metadata#users('${encodeURIComponent(userKey)}')/${collection}('${msgId}')/attachmentSessions/$entity`,
4611
+ expirationDateTime: rec.expirationDateTime,
4612
+ nextExpectedRanges: [String(rec.received)]
4613
+ }
4614
+ };
4615
+ }
4616
+ const data = Buffer.concat(rec.chunks.map((s) => Buffer.from(s, "base64")));
4617
+ const a = {
4618
+ counter: ex.alloc(mbx),
4619
+ type: "file",
4620
+ name: rec.name,
4621
+ contentType: rec.contentType,
4622
+ isInline: rec.isInline,
4623
+ contentId: rec.contentId,
4624
+ contentLocation: null,
4625
+ contentBytes: data.toString("base64"),
4626
+ item: null,
4627
+ // One observation (upload-session): 41 + 6 per name character + 2 per content type character.
4628
+ size: data.length + 41 + 6 * rec.name.length + 2 * rec.contentType.length,
4629
+ lastModifiedDateTime: isoSeconds()
4630
+ };
4631
+ item.attachments.push(a);
4632
+ if (isEvent) {
4633
+ touchEvent(ex, mbx, item);
4634
+ } else {
4635
+ ex.touch(mbx, item);
4636
+ }
4637
+ rec.completed = true;
4638
+ rec.chunks = [];
4639
+ delete ex.state.uploadSessions[rec.id];
4640
+ return {
4641
+ status: 201,
4642
+ headers: {
4643
+ Location: `${baseUrl}/api/gv1.0/users('${userKey}')/${collection}('${msgId}')/Attachments('${attachmentId(ex, mbx, item, a)}')`,
4644
+ "request-id": requestId
4645
+ },
4646
+ body: null
4647
+ };
4648
+ }
4649
+
4650
+ export {
4651
+ GraphError,
4652
+ TodoErrors,
4653
+ Errors,
4654
+ wwwAuthenticate,
4655
+ exchangeBase64,
4656
+ fromExchangeBase64,
4657
+ encodeFolderId,
4658
+ encodeItemId,
4659
+ immutableEntryIdBytes,
4660
+ restImmutableFromEntryId,
4661
+ foreignEntryIdToRest,
4662
+ encodeImmutableId,
4663
+ decodeId,
4664
+ encodeChangeKey,
4665
+ newConversationIndex,
4666
+ childConversationIndex,
4667
+ newInternetMessageId,
4668
+ urlSafeBinary,
4669
+ fromUrlSafeBinary,
4670
+ entryIdBytes,
4671
+ parseEntryId,
4672
+ isoSeconds,
4673
+ Exchange,
4674
+ getExchange,
4675
+ ATTENDEE_EMPTY_BODY,
4676
+ HTML_HEAD,
4677
+ escapeHtml,
4678
+ bodyInner,
4679
+ storeBody,
4680
+ htmlToText,
4681
+ bodyAsText,
4682
+ bodyAsHtml,
4683
+ bodyPreview,
4684
+ quoteBody,
4685
+ commentBody,
4686
+ resolveZone,
4687
+ offsetAt,
4688
+ parseLocal,
4689
+ wallToUtc,
4690
+ utcToWall,
4691
+ formatGraphDateTime,
4692
+ displayZoneName,
4693
+ DAY,
4694
+ ZERO_TIME,
4695
+ occurrenceGoid,
4696
+ isCalendarFolder,
4697
+ defaultCalendar,
4698
+ calendarGroups,
4699
+ groupOfCalendar,
4700
+ calendarFolders,
4701
+ calendarId,
4702
+ calendarByNavId,
4703
+ calendarMeta,
4704
+ CALENDAR_COLORS,
4705
+ eventsOf,
4706
+ eventChangeKey,
4707
+ eventLongChangeKey,
4708
+ eventId,
4709
+ occurrenceId,
4710
+ occurrenceKey,
4711
+ eventsIn,
4712
+ emptyLocation,
4713
+ newEvent,
4714
+ createdStamp,
4715
+ touchEvent,
4716
+ purgeEvent,
4717
+ deleteEvent,
4718
+ exceptionsOf,
4719
+ dateOnly,
4720
+ formatDate,
4721
+ localDate,
4722
+ expandSeries,
4723
+ occurrenceOn,
4724
+ firstSeriesDate,
4725
+ occurrenceView,
4726
+ json,
4727
+ noContent,
4728
+ errorResponse,
4729
+ newRequestId,
4730
+ parseJsonBody,
4731
+ parsePrefer,
4732
+ contextUserKey,
4733
+ expectString,
4734
+ expectBoolean,
4735
+ expectEnum,
4736
+ expectObject,
4737
+ expectArray,
4738
+ actionParams,
4739
+ checkKeys,
4740
+ parseRecipient,
4741
+ parseRecipients,
4742
+ parseMessageInput,
4743
+ MESSAGE_PROPERTIES,
4744
+ messagePathType,
4745
+ importanceRank,
4746
+ messageValue,
4747
+ webLink,
4748
+ canonicalMessageSelect,
4749
+ renderMessage,
4750
+ FOLDER_SELECTABLE,
4751
+ renderFolder,
4752
+ canonicalFolderSelect,
4753
+ folderValue,
4754
+ folderPathType,
4755
+ attachmentId,
4756
+ decodeAttachmentId,
4757
+ renderAttachment,
4758
+ createUploadSession,
4759
+ cancelUploadSession,
4760
+ uploadChunk
4761
+ };
4762
+ //# sourceMappingURL=chunk-BEGIZZSN.js.map