@remit/web-client 0.0.303 → 0.0.305

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.
@@ -458,3 +458,93 @@ export const SyncStalled: Story = {
458
458
  );
459
459
  },
460
460
  };
461
+
462
+ const OUTLOOK = makeAccount({
463
+ accountId: "acc-outlook",
464
+ email: "alice@outlook.com",
465
+ authType: "oauthMicrosoft",
466
+ syncedServices: ["Calendar"],
467
+ grantedScopes: ["https://graph.microsoft.com/Calendars.Read"],
468
+ });
469
+
470
+ const withOutlook = mailWorld({
471
+ accounts: [...mailWorld().accounts, OUTLOOK],
472
+ });
473
+
474
+ export const MicrosoftServiceChoice: Story = {
475
+ play: async ({ canvasElement }) => {
476
+ const canvas = within(canvasElement);
477
+ await toMicrosoft(canvas);
478
+ await expect(canvas.getByRole("checkbox", { name: /^Mail/ })).toBeChecked();
479
+ await expect(
480
+ canvas.getByRole("checkbox", { name: /^Calendar/ }),
481
+ ).toBeChecked();
482
+ },
483
+ };
484
+
485
+ export const MicrosoftNothingPicked: Story = {
486
+ play: async ({ canvasElement }) => {
487
+ const canvas = within(canvasElement);
488
+ const signIn = await toMicrosoft(canvas);
489
+ await userEvent.click(canvas.getByRole("checkbox", { name: /^Mail/ }));
490
+ await userEvent.click(canvas.getByRole("checkbox", { name: /^Calendar/ }));
491
+ await userEvent.click(signIn);
492
+ await expect(await canvas.findByRole("alert")).toHaveTextContent(
493
+ "Pick at least one",
494
+ );
495
+ await expect(signIn).toBeEnabled();
496
+ },
497
+ };
498
+
499
+ export const MicrosoftGranted: Story = {
500
+ args: { url: `/settings/accounts?connected=${OUTLOOK.accountId}` },
501
+ parameters: handlers({}, withOutlook),
502
+ play: async ({ canvasElement }) => {
503
+ const canvas = within(canvasElement);
504
+ await expect(
505
+ await canvas.findByText(`Connected ${OUTLOOK.email}`),
506
+ ).toBeVisible();
507
+ await expect(canvas.getByText("Access granted — syncing")).toBeVisible();
508
+ await expect(
509
+ canvas.getByRole("button", { name: "Continue" }),
510
+ ).toBeEnabled();
511
+ },
512
+ };
513
+
514
+ export const MicrosoftScopeNotGranted: Story = {
515
+ args: {
516
+ url: "/settings/accounts?oauthError=scope_not_granted&oauthEmail=bob%40outlook.com&missingServices=Calendar",
517
+ },
518
+ parameters: handlers({}, mailWorld()),
519
+ play: async ({ canvasElement }) => {
520
+ const canvas = within(canvasElement);
521
+ await expect(
522
+ await canvas.findByText(/did not grant access to Calendar/),
523
+ ).toBeVisible();
524
+ await expect(canvas.getByLabelText("Email address (optional)")).toHaveValue(
525
+ "bob@outlook.com",
526
+ );
527
+ await expect(
528
+ canvas.getByRole("button", { name: "Sign in with Microsoft" }),
529
+ ).toBeEnabled();
530
+ },
531
+ };
532
+
533
+ export const MicrosoftReconnectScopeNotGranted: Story = {
534
+ args: {
535
+ url: `/settings/accounts?oauthError=scope_not_granted&oauthEmail=${encodeURIComponent(OUTLOOK.email)}&missingServices=Calendar`,
536
+ },
537
+ parameters: handlers({}, withOutlook),
538
+ play: async ({ canvasElement }) => {
539
+ const canvas = within(canvasElement);
540
+ await expect(
541
+ await canvas.findByText(`Reconnect ${OUTLOOK.email}`),
542
+ ).toBeVisible();
543
+ await expect(
544
+ canvas.getByText(/did not grant access to Calendar/),
545
+ ).toBeVisible();
546
+ await expect(canvas.getByLabelText("Email address (optional)")).toHaveValue(
547
+ OUTLOOK.email,
548
+ );
549
+ },
550
+ };
@@ -130,7 +130,10 @@ describe("AccountServices", () => {
130
130
  const mounted = await mount(outlook());
131
131
 
132
132
  mounted.click(switchFor(mounted, "calendar"));
133
- await mounted.waitFor(() => patches().length > 0, "the save to start");
133
+ await mounted.waitFor(
134
+ () => patches().length > 0 && switchFor(mounted, "calendar").disabled,
135
+ "the save to start",
136
+ );
134
137
 
135
138
  assert.equal(checked(mounted, "calendar"), "true");
136
139
  assert.equal(switchFor(mounted, "mail").disabled, true);
@@ -199,6 +199,11 @@ export const AccountsOauthSuccess: Story = {
199
199
  args: { url: "/settings/accounts?connected=acc-personal" },
200
200
  play: async ({ canvasElement }) => {
201
201
  const canvas = within(canvasElement);
202
+ await expect(await canvas.findByText(/^Connected /)).toBeVisible();
203
+ await expect(canvas.getByText(/Access granted/)).toBeVisible();
204
+ await userEvent.click(
205
+ canvas.getByRole("button", { name: /Go to inbox|Continue/ }),
206
+ );
202
207
  await expect(
203
208
  await canvas.findByText("Account connected successfully."),
204
209
  ).toBeVisible();
@@ -0,0 +1,191 @@
1
+ import type {
2
+ RemitImapOutboxMessageResponse,
3
+ RemitImapThreadMessageResponse,
4
+ } from "@remit/api-http-client/types.gen.ts";
5
+ import {
6
+ DAY,
7
+ HOUR,
8
+ type MailWorld,
9
+ MINUTE,
10
+ mailboxIdFor,
11
+ mailWorld,
12
+ outboxMessage,
13
+ threadRow,
14
+ WORK,
15
+ } from "./mail-world";
16
+
17
+ export const INBOX = mailboxIdFor(WORK, "Inbox");
18
+
19
+ export const LUNCH_SUBJECT = "Lunch Thursday?";
20
+ export const LUNCH_BODY = "Are we still on for Thursday? I can do 12:30.";
21
+ export const LONG_SUBJECT = "Billing migration owners";
22
+ export const TURNS_SUBJECT = "Offsite venue";
23
+ export const SCANS_SUBJECT = "Scans";
24
+ export const MISSPELT_DRAFT =
25
+ "<p>Ths report is redy today, and the notes are attachd.</p>";
26
+ export const PLAIN_DRAFT = [
27
+ "Thanks, that works for me.",
28
+ "",
29
+ "| Region | Total |",
30
+ "| --- | --- |",
31
+ "| EMEA | 412 |",
32
+ ].join("\n");
33
+
34
+ const longBody = [
35
+ "Longer context, so the pane has more message than it has room for.",
36
+ ...Array.from(
37
+ { length: 14 },
38
+ (_, index) =>
39
+ `Point ${index + 1}. The billing migration touches the export path, the dunning schedule and the invoice numbering, and each of those has a different owner today.`,
40
+ ),
41
+ ].join("\n\n");
42
+
43
+ const longDraft = [
44
+ "<p>That works. Before Thursday, three things I want written down:</p>",
45
+ ...Array.from(
46
+ { length: 24 },
47
+ (_, index) =>
48
+ `<p>Point ${index + 1}. Whoever owns the dunning mail after self-serve ships also owns the invoice numbering, and today those are two different people on two different rotas.</p>`,
49
+ ),
50
+ ].join("");
51
+
52
+ const conversations: RemitImapThreadMessageResponse[] = [
53
+ threadRow({
54
+ id: "lunch",
55
+ accountId: WORK,
56
+ fromName: "Ada Lovelace",
57
+ fromEmail: "ada@acme.example",
58
+ subject: LUNCH_SUBJECT,
59
+ snippet: LUNCH_BODY,
60
+ category: "personal",
61
+ ago: 30 * MINUTE,
62
+ }),
63
+ threadRow({
64
+ id: "billing",
65
+ accountId: WORK,
66
+ fromName: "Priya Raman",
67
+ fromEmail: "priya@acme.example",
68
+ subject: LONG_SUBJECT,
69
+ snippet:
70
+ "Longer context, so the pane has more message than it has room for.",
71
+ category: "personal",
72
+ ago: 3 * HOUR,
73
+ }),
74
+ threadRow({
75
+ id: "venue-1",
76
+ inThread: "venue",
77
+ accountId: WORK,
78
+ role: "Archive",
79
+ fromName: "Linus Berg",
80
+ fromEmail: "linus@acme.example",
81
+ subject: TURNS_SUBJECT,
82
+ snippet: "Two options for the offsite: the boathouse or the old mill.",
83
+ category: "personal",
84
+ ago: 3 * DAY,
85
+ isRead: true,
86
+ }),
87
+ threadRow({
88
+ id: "venue-2",
89
+ inThread: "venue",
90
+ accountId: WORK,
91
+ role: "Sent",
92
+ fromName: "Alice Tan",
93
+ fromEmail: "alice.tan@acme.example",
94
+ subject: `Re: ${TURNS_SUBJECT}`,
95
+ snippet: "The boathouse, if it still has the long table.",
96
+ category: "personal",
97
+ ago: 2 * DAY,
98
+ isRead: true,
99
+ }),
100
+ threadRow({
101
+ id: "venue-3",
102
+ inThread: "venue",
103
+ accountId: WORK,
104
+ fromName: "Linus Berg",
105
+ fromEmail: "linus@acme.example",
106
+ subject: `Re: ${TURNS_SUBJECT}`,
107
+ snippet: "It does. Shall I book it for the 14th?",
108
+ category: "personal",
109
+ ago: 4 * HOUR,
110
+ }),
111
+ threadRow({
112
+ id: "scans",
113
+ accountId: WORK,
114
+ fromName: "Office Scanner",
115
+ fromEmail: "scanner@acme.example",
116
+ subject: SCANS_SUBJECT,
117
+ snippet: "",
118
+ category: "automated",
119
+ ago: 5 * HOUR,
120
+ hasAttachment: true,
121
+ }),
122
+ ];
123
+
124
+ const drafts: RemitImapOutboxMessageResponse[] = [
125
+ {
126
+ ...outboxMessage({
127
+ id: "misspelt",
128
+ accountId: WORK,
129
+ to: "ada@acme.example",
130
+ subject: "Report",
131
+ body: "Ths report is redy today, and the notes are attachd.",
132
+ status: "draft",
133
+ ago: 5 * MINUTE,
134
+ }),
135
+ htmlBody: MISSPELT_DRAFT,
136
+ },
137
+ outboxMessage({
138
+ id: "plain",
139
+ accountId: WORK,
140
+ to: "ada@acme.example",
141
+ subject: "Re: Q3 planning",
142
+ body: PLAIN_DRAFT,
143
+ status: "draft",
144
+ ago: 6 * MINUTE,
145
+ }),
146
+ {
147
+ ...outboxMessage({
148
+ id: "long-reply",
149
+ accountId: WORK,
150
+ to: "ada@acme.example",
151
+ subject: `Re: ${LUNCH_SUBJECT}`,
152
+ body: "That works.",
153
+ status: "draft",
154
+ ago: 7 * MINUTE,
155
+ }),
156
+ htmlBody: longDraft,
157
+ },
158
+ ];
159
+
160
+ export const composeWorld = (overrides: Partial<MailWorld> = {}): MailWorld => {
161
+ const base = mailWorld();
162
+ return {
163
+ ...base,
164
+ accounts: base.accounts.map((account) =>
165
+ account.accountId === WORK
166
+ ? { ...account, composeLanguages: ["en", "nl", "de"] }
167
+ : account,
168
+ ),
169
+ threads: [...base.threads, ...conversations],
170
+ outbox: [...base.outbox, ...drafts],
171
+ bodies: { "msg-billing": longBody },
172
+ ...overrides,
173
+ };
174
+ };
175
+
176
+ export const withoutSmtp = (world: MailWorld): MailWorld => ({
177
+ ...world,
178
+ accounts: world.accounts.map((account) => ({
179
+ ...account,
180
+ smtpEnabled: false,
181
+ })),
182
+ });
183
+
184
+ export const messageUrl = (id: string, mailboxId: string = INBOX): string =>
185
+ `/mail/${mailboxId}/thread-${id}/msg-${id}`;
186
+
187
+ export const threadUrl = (
188
+ threadId: string,
189
+ messageId: string,
190
+ mailboxId: string = INBOX,
191
+ ): string => `/mail/${mailboxId}/thread-${threadId}/msg-${messageId}`;
@@ -2,19 +2,30 @@ import type {
2
2
  AccountDetailOperationsUpdateAccountResponse,
3
3
  AddressDetailOperationsUpdateAddressResponse,
4
4
  AddressOperationsSearchAddressesResponse,
5
+ CalendarEventOperationsListCalendarEventsResponse,
6
+ CalendarOperationsListCalendarsResponse,
7
+ CalendarSuggestionOperationsListCalendarSuggestionsResponse,
5
8
  FilterOperationsListFiltersResponse,
6
9
  LabelOperationsListLabelsResponse,
7
10
  MailboxDetailOperationsRenameMailboxResponse,
8
11
  MailboxOperationsListMailboxesResponse,
9
12
  MeOperationsListQuarantineResponse,
10
13
  MeOperationsListVipSuggestionsResponse,
14
+ MessageBulkOperationsDeleteMessagesData,
15
+ MessageBulkOperationsDeleteMessagesResponse,
16
+ MessageBulkOperationsMoveMessagesData,
17
+ MessageBulkOperationsMoveMessagesResponse,
11
18
  MessageBulkOperationsUpdateFlagsResponse,
12
19
  MessageCalendarSuggestionOperationsListMessageCalendarSuggestionsResponse,
20
+ OrganizeOperationsPreviewOrganizeResponse,
13
21
  OutboxOperationsListOutboxMessagesResponse,
14
22
  RemitImapConfigDescriptionResponse,
23
+ RemitImapCreateOutboxMessageInput,
15
24
  RemitImapDescribeMessageResponse,
25
+ RemitImapOutboxMessageResponse,
16
26
  RemitImapThreadMessageResponse,
17
27
  RemitImapThreadSearchResponse,
28
+ RemitImapUpdateOutboxMessageInput,
18
29
  SemanticSearchOperationsSemanticSearchResponse,
19
30
  SyncOperationsGetSyncStatusResponse,
20
31
  SyncOperationsTriggerSyncResponse,
@@ -32,11 +43,45 @@ export interface MailHandlerOptions {
32
43
  withholdCounts?: boolean;
33
44
  pageSize?: number;
34
45
  holdLaterPages?: boolean;
46
+ draftSave?: "saves" | "refused";
47
+ send?: "sends" | "holds";
48
+ search?: "answers" | "holds";
49
+ config?: "answers" | "holds";
35
50
  }
36
51
 
37
52
  const contentPath = (row: RemitImapThreadMessageResponse): string =>
38
53
  `/content/accounts/${row.accountConfigId}/${row.accountId}/messages/${row.messageId}/parts/1`;
39
54
 
55
+ const attachmentOnly = (row: RemitImapThreadMessageResponse): boolean =>
56
+ row.hasAttachment && !row.snippet;
57
+
58
+ const bodyPartsOf = (
59
+ row: RemitImapThreadMessageResponse,
60
+ ): RemitImapDescribeMessageResponse["bodyParts"] =>
61
+ attachmentOnly(row)
62
+ ? [
63
+ {
64
+ bodyPartId: `part-${row.messageId}`,
65
+ mediaType: "APPLICATION",
66
+ mediaSubtype: "pdf",
67
+ sizeOctets: 48_000,
68
+ isMultipart: false,
69
+ disposition: "attachment",
70
+ dispositionFilename: "scan.pdf",
71
+ contentUrl: contentPath(row),
72
+ },
73
+ ]
74
+ : [
75
+ {
76
+ bodyPartId: `part-${row.messageId}`,
77
+ mediaType: "TEXT",
78
+ mediaSubtype: "plain",
79
+ sizeOctets: (row.snippet ?? "").length,
80
+ isMultipart: false,
81
+ contentUrl: contentPath(row),
82
+ },
83
+ ];
84
+
40
85
  const describe = (
41
86
  row: RemitImapThreadMessageResponse,
42
87
  ): RemitImapDescribeMessageResponse => ({
@@ -71,16 +116,7 @@ const describe = (
71
116
  senderTrust: row.senderTrust,
72
117
  },
73
118
  flags: row.isRead ? ["\\Seen"] : [],
74
- bodyParts: [
75
- {
76
- bodyPartId: `part-${row.messageId}`,
77
- mediaType: "TEXT",
78
- mediaSubtype: "plain",
79
- sizeOctets: (row.snippet ?? "").length,
80
- isMultipart: false,
81
- contentUrl: contentPath(row),
82
- },
83
- ],
119
+ bodyParts: bodyPartsOf(row),
84
120
  references: [],
85
121
  });
86
122
 
@@ -167,11 +203,23 @@ export const mailHandlers = (
167
203
  ),
168
204
  );
169
205
 
206
+ const outbox = new Map(
207
+ world.outbox.map((message) => [message.outboxMessageId, message]),
208
+ );
209
+ let draftsCreated = 0;
210
+ const refuseSave = () =>
211
+ HttpResponse.json(
212
+ { status: 409, message: "The mail server did not accept the draft." },
213
+ { status: 409 },
214
+ );
215
+
170
216
  return [
171
217
  http.get(`${API}/config`, () =>
172
- HttpResponse.json<RemitImapConfigDescriptionResponse>(
173
- makeConfig(world.accounts),
174
- ),
218
+ options.config === "holds"
219
+ ? pending()
220
+ : HttpResponse.json<RemitImapConfigDescriptionResponse>(
221
+ makeConfig(world.accounts),
222
+ ),
175
223
  ),
176
224
  http.patch(
177
225
  `${API}/accounts/:accountId/mailboxes/:mailboxId`,
@@ -277,11 +325,38 @@ export const mailHandlers = (
277
325
  failureCount: 0,
278
326
  }),
279
327
  ),
328
+ http.post(`${API}/messages/delete`, async ({ request }) => {
329
+ const { messageIds } =
330
+ (await request.json()) as MessageBulkOperationsDeleteMessagesData["body"];
331
+ return HttpResponse.json<MessageBulkOperationsDeleteMessagesResponse>({
332
+ successCount: messageIds.length,
333
+ failureCount: 0,
334
+ });
335
+ }),
336
+ http.post(`${API}/messages/move`, async ({ request }) => {
337
+ const { messageIds } =
338
+ (await request.json()) as MessageBulkOperationsMoveMessagesData["body"];
339
+ return HttpResponse.json<MessageBulkOperationsMoveMessagesResponse>({
340
+ successCount: messageIds.length,
341
+ failureCount: 0,
342
+ });
343
+ }),
344
+ http.post(`${API}/accounts/:accountId/organize/preview`, ({ params }) => {
345
+ const matched = world.threads.filter(
346
+ (row) => row.accountId === params.accountId,
347
+ );
348
+ return HttpResponse.json<OrganizeOperationsPreviewOrganizeResponse>({
349
+ matchedCount: matched.length,
350
+ messageIds: matched.map((row) => row.messageId),
351
+ });
352
+ }),
280
353
  http.get(
281
354
  "/content/accounts/:accountConfigId/:accountId/messages/:messageId/parts/*",
282
355
  ({ params }) =>
283
356
  HttpResponse.text(
284
- byMessageId.get(String(params.messageId))?.snippet ?? "",
357
+ world.bodies[String(params.messageId)] ??
358
+ byMessageId.get(String(params.messageId))?.snippet ??
359
+ "",
285
360
  ),
286
361
  ),
287
362
  http.get(`${API}/threads/:threadId/messages`, ({ params }) =>
@@ -300,21 +375,25 @@ export const mailHandlers = (
300
375
  );
301
376
  if (options.holdLaterPages && params.has("continuationToken"))
302
377
  return pending();
378
+ if (searching && options.search === "holds") return pending();
303
379
  return HttpResponse.json(listing(narrow(scope, params), params, options));
304
380
  }),
305
381
  http.get(
306
382
  `${API}/mailboxes/:mailboxId/threads/search`,
307
- ({ request, params }) =>
308
- HttpResponse.json(
383
+ ({ request, params }) => {
384
+ const search = new URL(request.url).searchParams;
385
+ if (search.has("query") && options.search === "holds") return pending();
386
+ return HttpResponse.json(
309
387
  listing(
310
388
  narrow(
311
389
  world.threads.filter((row) => row.mailboxId === params.mailboxId),
312
- new URL(request.url).searchParams,
390
+ search,
313
391
  ),
314
- new URL(request.url).searchParams,
392
+ search,
315
393
  options,
316
394
  ),
317
- ),
395
+ );
396
+ },
318
397
  ),
319
398
  http.get(`${API}/mailboxes/:mailboxId/threads`, ({ params }) =>
320
399
  HttpResponse.json<ThreadOperationsListThreadsResponse>({
@@ -325,14 +404,108 @@ export const mailHandlers = (
325
404
  ),
326
405
  http.get(`${API}/outbox`, () =>
327
406
  HttpResponse.json<OutboxOperationsListOutboxMessagesResponse>({
328
- items: world.outbox,
407
+ items: [...outbox.values()],
329
408
  }),
330
409
  ),
331
- http.get(`${API}/search/semantic`, () =>
332
- HttpResponse.json<SemanticSearchOperationsSemanticSearchResponse>({
410
+ http.post(`${API}/outbox`, async ({ request }) => {
411
+ if (options.draftSave === "refused") return refuseSave();
412
+ const input = (await request.json()) as RemitImapCreateOutboxMessageInput;
413
+ draftsCreated += 1;
414
+ const account = world.accounts.find(
415
+ (candidate) => candidate.accountId === input.accountId,
416
+ );
417
+ const created: RemitImapOutboxMessageResponse = {
418
+ outboxMessageId: `out-created-${draftsCreated}`,
419
+ accountId: input.accountId,
420
+ fromAddress: account?.email ?? "",
421
+ fromName: account?.displayName,
422
+ toAddresses: input.toAddresses,
423
+ ccAddresses: input.ccAddresses ?? [],
424
+ bccAddresses: input.bccAddresses ?? [],
425
+ subject: input.subject,
426
+ textBody: input.textBody,
427
+ htmlBody: input.htmlBody,
428
+ references: input.references ?? [],
429
+ status: "draft",
430
+ createdAt: Date.UTC(2026, 8, 24, 13, 0),
431
+ updatedAt: Date.UTC(2026, 8, 24, 13, 0),
432
+ attachments: [],
433
+ };
434
+ outbox.set(created.outboxMessageId, created);
435
+ return HttpResponse.json<RemitImapOutboxMessageResponse>(created);
436
+ }),
437
+ http.get(`${API}/outbox/:outboxMessageId`, ({ params }) => {
438
+ const message = outbox.get(String(params.outboxMessageId));
439
+ if (!message)
440
+ return HttpResponse.json(
441
+ { status: 404, message: "Outbox message not found" },
442
+ { status: 404 },
443
+ );
444
+ return HttpResponse.json<RemitImapOutboxMessageResponse>(message);
445
+ }),
446
+ http.patch(
447
+ `${API}/outbox/:outboxMessageId`,
448
+ async ({ params, request }) => {
449
+ if (options.draftSave === "refused") return refuseSave();
450
+ const id = String(params.outboxMessageId);
451
+ const current = outbox.get(id);
452
+ if (!current)
453
+ return HttpResponse.json(
454
+ { status: 404, message: "Outbox message not found" },
455
+ { status: 404 },
456
+ );
457
+ const input =
458
+ (await request.json()) as RemitImapUpdateOutboxMessageInput;
459
+ const { attachmentIds: _attachmentIds, ...fields } = input;
460
+ const updated: RemitImapOutboxMessageResponse = {
461
+ ...current,
462
+ ...fields,
463
+ status: "draft",
464
+ };
465
+ outbox.set(id, updated);
466
+ return HttpResponse.json<RemitImapOutboxMessageResponse>(updated);
467
+ },
468
+ ),
469
+ http.post(`${API}/outbox/:outboxMessageId/send`, ({ params }) => {
470
+ if (options.send === "holds") return pending();
471
+ const id = String(params.outboxMessageId);
472
+ const current = outbox.get(id);
473
+ if (!current)
474
+ return HttpResponse.json(
475
+ { status: 404, message: "Outbox message not found" },
476
+ { status: 404 },
477
+ );
478
+ const queued: RemitImapOutboxMessageResponse = {
479
+ ...current,
480
+ status: "queued",
481
+ };
482
+ outbox.set(id, queued);
483
+ return HttpResponse.json<RemitImapOutboxMessageResponse>(queued);
484
+ }),
485
+ http.delete(`${API}/outbox/:outboxMessageId`, ({ params }) => {
486
+ outbox.delete(String(params.outboxMessageId));
487
+ return new HttpResponse(null, { status: 204 });
488
+ }),
489
+ http.get(`${API}/calendars`, () =>
490
+ HttpResponse.json<CalendarOperationsListCalendarsResponse>({ items: [] }),
491
+ ),
492
+ http.get(`${API}/calendar-events`, () =>
493
+ HttpResponse.json<CalendarEventOperationsListCalendarEventsResponse>({
333
494
  items: [],
334
495
  }),
335
496
  ),
497
+ http.get(`${API}/calendar-suggestions`, () =>
498
+ HttpResponse.json<CalendarSuggestionOperationsListCalendarSuggestionsResponse>(
499
+ { items: [] },
500
+ ),
501
+ ),
502
+ http.get(`${API}/search/semantic`, () =>
503
+ options.search === "holds"
504
+ ? pending()
505
+ : HttpResponse.json<SemanticSearchOperationsSemanticSearchResponse>({
506
+ items: [],
507
+ }),
508
+ ),
336
509
  http.get(`${API}/me/quarantine`, () =>
337
510
  HttpResponse.json<MeOperationsListQuarantineResponse>({
338
511
  entries: world.quarantine,