fluxmail 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/accounts/googleAuth.d.ts +5 -1
  2. package/dist/accounts/googleAuth.d.ts.map +1 -1
  3. package/dist/accounts/googleAuth.js +5 -1
  4. package/dist/accounts/googleAuth.js.map +1 -1
  5. package/dist/accounts/registry.d.ts +1 -0
  6. package/dist/accounts/registry.d.ts.map +1 -1
  7. package/dist/accounts/registry.js +11 -0
  8. package/dist/accounts/registry.js.map +1 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +69 -0
  11. package/dist/cli.js.map +1 -1
  12. package/dist/cliMail.d.ts +1 -0
  13. package/dist/cliMail.d.ts.map +1 -1
  14. package/dist/cliMail.js +71 -2
  15. package/dist/cliMail.js.map +1 -1
  16. package/dist/http/rest.d.ts.map +1 -1
  17. package/dist/http/rest.js +241 -12
  18. package/dist/http/rest.js.map +1 -1
  19. package/dist/mcp/buildServer.d.ts +1 -0
  20. package/dist/mcp/buildServer.d.ts.map +1 -1
  21. package/dist/mcp/buildServer.js +73 -3
  22. package/dist/mcp/buildServer.js.map +1 -1
  23. package/dist/permissions.js +1 -1
  24. package/dist/permissions.js.map +1 -1
  25. package/dist/scheduler/sendScheduler.js +1 -1
  26. package/dist/scheduler/sendScheduler.js.map +1 -1
  27. package/dist/service/emailService.d.ts +35 -2
  28. package/dist/service/emailService.d.ts.map +1 -1
  29. package/dist/service/emailService.js +270 -35
  30. package/dist/service/emailService.js.map +1 -1
  31. package/dist/service/searchCursor.d.ts +2 -0
  32. package/dist/service/searchCursor.d.ts.map +1 -1
  33. package/dist/service/searchCursor.js +4 -1
  34. package/dist/service/searchCursor.js.map +1 -1
  35. package/dist/storage/db.d.ts +66 -2
  36. package/dist/storage/db.d.ts.map +1 -1
  37. package/dist/storage/db.js +25 -1
  38. package/dist/storage/db.js.map +1 -1
  39. package/dist/storage/sendAs.d.ts +9 -0
  40. package/dist/storage/sendAs.d.ts.map +1 -0
  41. package/dist/storage/sendAs.js +26 -0
  42. package/dist/storage/sendAs.js.map +1 -0
  43. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../src/http/rest.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAC;AAGhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAWtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAa,MAAM,4BAA4B,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAA2C,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAIrF,OAAO,EAA2C,KAAK,SAAS,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAMpH,OAAO,EAA+B,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAEzE,UAAU,aAAa;IACrB,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,EAAE,YAAY,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED,KAAK,OAAO,GAAG;IAAE,QAAQ,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,aAAa,CAAA;CAAE,CAAC;AAGpE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,cAAc,CAAC;IACvB,aAAa,EAAE,oBAAoB,CAAC;IACpC,EAAE,EAAE,UAAU,CAAC;IACf,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAkPD,eAAO,MAAM,qBAAqB;;;;;;;CAOjC,CAAC;AAiiBF,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,CA6tBrE"}
1
+ {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../src/http/rest.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAC;AAGhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAYtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAa,MAAM,4BAA4B,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAA2C,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAIrF,OAAO,EAA2C,KAAK,SAAS,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAMpH,OAAO,EAA+B,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAEzE,UAAU,aAAa;IACrB,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,EAAE,YAAY,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED,KAAK,OAAO,GAAG;IAAE,QAAQ,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,aAAa,CAAA;CAAE,CAAC;AAGpE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,cAAc,CAAC;IACvB,aAAa,EAAE,oBAAoB,CAAC;IACpC,EAAE,EAAE,UAAU,CAAC;IACf,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAkSD,eAAO,MAAM,qBAAqB;;;;;;;CAOjC,CAAC;AAgkBF,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,CA43BrE"}
package/dist/http/rest.js CHANGED
@@ -18,7 +18,7 @@ const messageId = id.openapi({ example: 'msg_123' });
18
18
  const threadId = id.openapi({ example: 'thread_123' });
19
19
  const draftId = id.openapi({ example: 'draft_123' });
20
20
  const scheduleId = id.openapi({ example: 'schedule_123' });
21
- const attachmentId = id.openapi({ example: 'attachment_123' });
21
+ const attachmentId = id.describe('Opaque attachment ID returned by message metadata').openapi({ example: 'part:1.2' });
22
22
  const isoDate = z
23
23
  .string()
24
24
  .regex(/^\d{4}-\d{2}-\d{2}$/, 'Expected an ISO date in YYYY-MM-DD format')
@@ -91,6 +91,24 @@ const LabelSchema = z
91
91
  })
92
92
  .strict()
93
93
  .openapi('Label');
94
+ const SendAsIdentitySchema = z
95
+ .object({
96
+ email: z.string().email(),
97
+ name: z.string().optional(),
98
+ replyTo: z.string().email().optional(),
99
+ isPrimary: z.boolean(),
100
+ source: z.enum(['provider', 'configured']),
101
+ })
102
+ .strict()
103
+ .openapi('SendAsIdentity');
104
+ const ConfiguredSendAsSchema = z
105
+ .object({ email: z.string().email(), name: z.string().optional() })
106
+ .strict()
107
+ .openapi('ConfiguredSendAs');
108
+ const ReplaceSendAsRequestSchema = z
109
+ .object({ identities: z.array(ConfiguredSendAsSchema) })
110
+ .strict()
111
+ .openapi('ReplaceSendAsRequest');
94
112
  const MessageSchema = z
95
113
  .object({
96
114
  id: z.string(),
@@ -214,23 +232,49 @@ const ErrorSchema = z
214
232
  function dataEnvelope(schema) {
215
233
  return z.object({ data: schema, warnings: z.array(z.string()).optional() }).strict();
216
234
  }
235
+ const SearchPageMetaSchema = z
236
+ .object({
237
+ nextPageToken: z.string().optional(),
238
+ exhausted: z.boolean(),
239
+ diagnostics: z.array(SearchDiagnosticSchema).optional(),
240
+ incomplete: z.literal(true).optional(),
241
+ incompleteReason: z.enum(['scan_limit', 'provider_limit', 'time_limit']).optional(),
242
+ inspectedCandidates: z.number().int().nonnegative().optional(),
243
+ })
244
+ .strict()
245
+ .openapi('SearchPageMeta');
217
246
  function pagedEnvelope(schema) {
218
247
  return z
219
248
  .object({
220
249
  data: z.array(schema),
221
- meta: z
222
- .object({
223
- nextPageToken: z.string().optional(),
224
- diagnostics: z.array(SearchDiagnosticSchema).optional(),
225
- incomplete: z.literal(true).optional(),
226
- incompleteReason: z.enum(['scan_limit', 'provider_limit']).optional(),
227
- inspectedCandidates: z.number().int().nonnegative().optional(),
228
- })
229
- .strict(),
250
+ meta: SearchPageMetaSchema,
230
251
  warnings: z.array(z.string()).optional(),
231
252
  })
232
253
  .strict();
233
254
  }
255
+ const BatchSearchResponseSchema = z
256
+ .object({
257
+ data: z.array(z.union([
258
+ z.object({ accountId: z.string(), data: z.array(MessageSchema), meta: SearchPageMetaSchema }).strict(),
259
+ z
260
+ .object({
261
+ accountId: z.string(),
262
+ error: z
263
+ .object({
264
+ code: z.string(),
265
+ message: z.string(),
266
+ data: z.record(z.unknown()).optional(),
267
+ exhausted: z.literal(false),
268
+ })
269
+ .strict(),
270
+ })
271
+ .strict(),
272
+ ])),
273
+ meta: z.object({ exhausted: z.boolean() }).strict(),
274
+ warnings: z.array(z.string()).optional(),
275
+ })
276
+ .strict()
277
+ .openapi('BatchSearchResponse');
234
278
  const errorResponses = {
235
279
  400: { content: { 'application/json': { schema: ErrorSchema } }, description: 'Invalid request' },
236
280
  401: { content: { 'application/json': { schema: ErrorSchema } }, description: 'Authentication required' },
@@ -262,6 +306,7 @@ const idempotencyHeaders = z.object({
262
306
  .describe('A unique key for one intended delivery. Reuse it when retrying the same request.'),
263
307
  });
264
308
  const draftShape = {
309
+ from: z.string().email().optional(),
265
310
  to: z.array(EmailAddressSchema).optional(),
266
311
  cc: z.array(EmailAddressSchema).optional(),
267
312
  bcc: z.array(EmailAddressSchema).optional(),
@@ -285,6 +330,7 @@ const SendDraftSchema = z.object({ draftId, sendAt: z.string().datetime({ offset
285
330
  const SendRequestSchema = z.union([SendDraftSchema, SendContentSchema]).openapi('SendRequest');
286
331
  const ForwardRequestSchema = z
287
332
  .object({
333
+ from: z.string().email().optional(),
288
334
  to: z.array(EmailAddressSchema).min(1),
289
335
  cc: z.array(EmailAddressSchema).optional(),
290
336
  comment: z.string().optional(),
@@ -360,8 +406,32 @@ const messageQuerySchema = z
360
406
  .regex(/^(?:[1-9]|[1-9][0-9]|100)$/)
361
407
  .optional(),
362
408
  pageToken: z.string().min(1).optional(),
409
+ includeSnippet: z.enum(['true', 'false']).optional(),
363
410
  })
364
411
  .strict();
412
+ const portableFolder = z.enum(['inbox', 'sent', 'drafts', 'archive', 'spam', 'trash', 'all']);
413
+ const BatchSearchRequestSchema = z
414
+ .object({
415
+ accounts: z
416
+ .array(z.object({ accountId, pageToken: z.string().min(1).optional() }).strict())
417
+ .min(1)
418
+ .max(20),
419
+ query: z.string().min(1),
420
+ folder: portableFolder.optional(),
421
+ text: z.string().optional(),
422
+ from: z.string().optional(),
423
+ to: z.string().optional(),
424
+ subject: z.string().optional(),
425
+ read: z.boolean().optional(),
426
+ starred: z.boolean().optional(),
427
+ hasAttachment: z.boolean().optional(),
428
+ after: isoDate.optional(),
429
+ before: isoDate.optional(),
430
+ pageSize: z.number().int().min(1).max(100).optional(),
431
+ includeSnippet: z.boolean().optional(),
432
+ })
433
+ .strict()
434
+ .openapi('BatchSearchRequest');
365
435
  const MODIFY_CAPABILITIES = {
366
436
  markRead: 'mail.organize',
367
437
  markUnread: 'mail.organize',
@@ -538,7 +608,9 @@ async function runJson(c, deps, options, fn) {
538
608
  if (reservation) {
539
609
  completeIdempotencyKey(deps.db, { ...reservation, responseStatus: status, responseBody: body });
540
610
  }
541
- outcome = 'success';
611
+ outcome = result.operationFailed ? 'error' : 'success';
612
+ if (result.operationFailed)
613
+ errorCode = 'account_failure';
542
614
  return new Response(body, {
543
615
  status,
544
616
  headers: { 'content-type': 'application/json; charset=UTF-8', 'cache-control': 'no-store' },
@@ -643,6 +715,7 @@ async function runAttachment(c, deps, accountId, messageId, attachmentId) {
643
715
  function toSendInput(input) {
644
716
  return {
645
717
  body: input.body,
718
+ ...(input.from ? { from: input.from } : {}),
646
719
  ...(input.to ? { to: input.to } : {}),
647
720
  ...(input.cc ? { cc: input.cc } : {}),
648
721
  ...(input.bcc ? { bcc: input.bcc } : {}),
@@ -686,6 +759,7 @@ function toEmailQuery(input) {
686
759
  page: {
687
760
  ...(input.pageSize ? { pageSize: Number(input.pageSize) } : {}),
688
761
  ...(input.pageToken ? { pageToken: input.pageToken } : {}),
762
+ ...(input.includeSnippet !== undefined ? { includeSnippet: input.includeSnippet === 'true' } : {}),
689
763
  },
690
764
  };
691
765
  }
@@ -832,6 +906,40 @@ export function createRestApi(deps) {
832
906
  // Auditing must not replace the API response.
833
907
  }
834
908
  });
909
+ app.use('/api/v1/accounts/:accountId/send-as', async (c, next) => {
910
+ await next();
911
+ if (c.req.method !== 'PUT')
912
+ return;
913
+ const auth = c.get('restAuth');
914
+ if (!auth)
915
+ return;
916
+ let errorCode;
917
+ if (c.res.status >= 400) {
918
+ try {
919
+ const payload = (await c.res.clone().json());
920
+ if (typeof payload.error?.code === 'string')
921
+ errorCode = payload.error.code;
922
+ }
923
+ catch {
924
+ errorCode = 'internal';
925
+ }
926
+ }
927
+ try {
928
+ recordAdminAuditEvent(deps.db, {
929
+ operation: 'put /api/v1/accounts/:id/send-as',
930
+ outcome: c.res.status < 400 ? 'success' : 'error',
931
+ actorKeyId: auth.kind === 'api_key' ? auth.keyId : undefined,
932
+ actorSessionId: auth.kind === 'session' ? auth.sessionId : undefined,
933
+ actorMemberId: auth.memberId,
934
+ resourceType: 'account',
935
+ resourceId: c.req.param('accountId'),
936
+ ...(errorCode ? { errorCode } : {}),
937
+ });
938
+ }
939
+ catch {
940
+ // Auditing must not replace the API response.
941
+ }
942
+ });
835
943
  app.use('/api/v1/*', async (c, next) => {
836
944
  c.set('restLogger', deps.logger);
837
945
  if (c.req.path.startsWith('/api/v1/auth/') ||
@@ -859,7 +967,8 @@ export function createRestApi(deps) {
859
967
  }
860
968
  if (publicPaths.has(c.req.path))
861
969
  return next();
862
- const administrative = c.req.path.startsWith('/api/v1/admin/');
970
+ const administrative = c.req.path.startsWith('/api/v1/admin/') ||
971
+ (c.req.method === 'PUT' && /^\/api\/v1\/accounts\/[^/]+\/send-as$/.test(c.req.path));
863
972
  if (administrative) {
864
973
  c.header('cache-control', 'no-store');
865
974
  c.header('x-content-type-options', 'nosniff');
@@ -1005,6 +1114,54 @@ export function createRestApi(deps) {
1005
1114
  data: await c.get('restService').listLabels(accountId),
1006
1115
  }));
1007
1116
  });
1117
+ const listSendAsRoute = createRoute({
1118
+ method: 'get',
1119
+ path: '/api/v1/accounts/{accountId}/send-as',
1120
+ operationId: 'listSendAs',
1121
+ summary: 'List sender addresses',
1122
+ description: 'List sender addresses available for an email account.',
1123
+ request: { params: accountParams },
1124
+ ...protectedRoute,
1125
+ responses: {
1126
+ 200: {
1127
+ content: { 'application/json': { schema: dataEnvelope(z.array(SendAsIdentitySchema)) } },
1128
+ description: 'Sender addresses',
1129
+ },
1130
+ ...errorResponses,
1131
+ },
1132
+ });
1133
+ app.openapi(listSendAsRoute, (c) => {
1134
+ const { accountId } = c.req.valid('param');
1135
+ return runJson(c, deps, { operation: 'listSendAs', capabilities: ['mail.read'] }, async () => ({
1136
+ data: await c.get('restService').listSendAs(accountId),
1137
+ }));
1138
+ });
1139
+ const replaceSendAsRoute = createRoute({
1140
+ method: 'put',
1141
+ path: '/api/v1/accounts/{accountId}/send-as',
1142
+ operationId: 'replaceSendAs',
1143
+ summary: 'Replace configured sender addresses',
1144
+ description: 'Replace the configured Outlook or IMAP aliases for an email account.',
1145
+ request: {
1146
+ params: accountParams,
1147
+ body: { required: true, content: { 'application/json': { schema: ReplaceSendAsRequestSchema } } },
1148
+ },
1149
+ ...protectedRoute,
1150
+ responses: {
1151
+ 200: {
1152
+ content: { 'application/json': { schema: dataEnvelope(z.array(SendAsIdentitySchema)) } },
1153
+ description: 'Sender addresses replaced',
1154
+ },
1155
+ ...errorResponses,
1156
+ },
1157
+ });
1158
+ app.openapi(replaceSendAsRoute, (c) => {
1159
+ const { accountId } = c.req.valid('param');
1160
+ const { identities } = c.req.valid('json');
1161
+ return runJson(c, deps, { operation: 'replaceSendAs', capabilities: ['admin.accounts'], quota: false }, async () => ({
1162
+ data: await c.get('restService').replaceSendAs(accountId, identities),
1163
+ }));
1164
+ });
1008
1165
  const listMessagesRoute = createRoute({
1009
1166
  method: 'get',
1010
1167
  path: '/api/v1/accounts/{accountId}/messages',
@@ -1027,6 +1184,7 @@ export function createRestApi(deps) {
1027
1184
  data: result.items,
1028
1185
  meta: {
1029
1186
  ...(result.nextPageToken ? { nextPageToken: result.nextPageToken } : {}),
1187
+ exhausted: result.exhausted,
1030
1188
  ...([...diagnostics, ...(result.diagnostics ?? [])].length
1031
1189
  ? { diagnostics: [...diagnostics, ...(result.diagnostics ?? [])] }
1032
1190
  : {}),
@@ -1037,6 +1195,77 @@ export function createRestApi(deps) {
1037
1195
  };
1038
1196
  });
1039
1197
  });
1198
+ const batchSearchRoute = createRoute({
1199
+ method: 'post',
1200
+ path: '/api/v1/messages/search',
1201
+ operationId: 'searchMessages',
1202
+ summary: 'Search multiple accounts',
1203
+ description: 'Search up to 20 email accounts with one portable query.',
1204
+ request: {
1205
+ body: { content: { 'application/json': { schema: BatchSearchRequestSchema } }, required: true },
1206
+ },
1207
+ ...protectedRoute,
1208
+ responses: {
1209
+ 200: {
1210
+ content: { 'application/json': { schema: BatchSearchResponseSchema } },
1211
+ description: 'Grouped search results',
1212
+ },
1213
+ ...errorResponses,
1214
+ },
1215
+ });
1216
+ app.openapi(batchSearchRoute, (c) => runJson(c, deps, { operation: 'searchMessages', capabilities: ['mail.read'] }, async () => {
1217
+ const input = c.req.valid('json');
1218
+ const parsed = parseEmailSearch(input.query);
1219
+ if (!parsed.valid) {
1220
+ throw new EmailError('invalid_request', parsed.diagnostics.map((item) => item.message).join(' '), {
1221
+ diagnostics: parsed.diagnostics,
1222
+ });
1223
+ }
1224
+ const structured = {};
1225
+ for (const key of ['folder', 'text', 'from', 'to', 'subject', 'after', 'before']) {
1226
+ if (input[key] !== undefined)
1227
+ structured[key] = input[key];
1228
+ }
1229
+ for (const key of ['read', 'starred', 'hasAttachment']) {
1230
+ if (input[key] !== undefined)
1231
+ structured[key] = input[key];
1232
+ }
1233
+ const merged = mergeEmailQueries(parsed.query, structured);
1234
+ if (!merged.success) {
1235
+ throw new EmailError('invalid_request', merged.diagnostics.map((item) => item.message).join(' '), {
1236
+ diagnostics: merged.diagnostics,
1237
+ });
1238
+ }
1239
+ const result = await c.get('restService').searchMessagesBatch({
1240
+ accounts: input.accounts,
1241
+ query: merged.query,
1242
+ ...(input.pageSize !== undefined ? { pageSize: input.pageSize } : {}),
1243
+ ...(input.includeSnippet !== undefined ? { includeSnippet: input.includeSnippet } : {}),
1244
+ });
1245
+ return {
1246
+ data: result.groups.map((group) => {
1247
+ if (!group.page)
1248
+ return { accountId: group.accountId, error: group.error };
1249
+ const diagnostics = [...parsed.diagnostics, ...(group.page.diagnostics ?? [])];
1250
+ return {
1251
+ accountId: group.accountId,
1252
+ data: group.page.items,
1253
+ meta: {
1254
+ exhausted: group.page.exhausted,
1255
+ ...(group.page.nextPageToken ? { nextPageToken: group.page.nextPageToken } : {}),
1256
+ ...(diagnostics.length ? { diagnostics } : {}),
1257
+ ...(group.page.incomplete ? { incomplete: true } : {}),
1258
+ ...(group.page.incompleteReason ? { incompleteReason: group.page.incompleteReason } : {}),
1259
+ ...(group.page.inspectedCandidates !== undefined
1260
+ ? { inspectedCandidates: group.page.inspectedCandidates }
1261
+ : {}),
1262
+ },
1263
+ };
1264
+ }),
1265
+ meta: { exhausted: result.exhausted },
1266
+ operationFailed: result.groups.some((group) => group.error !== undefined),
1267
+ };
1268
+ }));
1040
1269
  const getMessageRoute = createRoute({
1041
1270
  method: 'get',
1042
1271
  path: '/api/v1/accounts/{accountId}/messages/{messageId}',