@remit/backend 0.0.144 → 0.0.146

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 (91) hide show
  1. package/dist/src/api.d.ts +0 -2
  2. package/dist/src/api.js +9 -80
  3. package/dist/src/auth.d.ts +13 -19
  4. package/dist/src/auth.js +51 -43
  5. package/dist/src/config/api-config.d.ts +20 -0
  6. package/dist/src/config/api-config.js +1 -0
  7. package/dist/src/config/from-env.d.ts +2 -0
  8. package/dist/src/config/from-env.js +65 -0
  9. package/dist/src/config/msoauth-secrets-manager.d.ts +2 -0
  10. package/dist/src/config/msoauth-secrets-manager.js +28 -0
  11. package/dist/src/config/msoauth.d.ts +18 -29
  12. package/dist/src/config/msoauth.js +4 -28
  13. package/dist/src/derive/contentSignature.d.ts +3 -4
  14. package/dist/src/derive/contentSignature.js +4 -13
  15. package/dist/src/derive/contentUrl.d.ts +0 -13
  16. package/dist/src/derive/contentUrl.js +0 -19
  17. package/dist/src/handlers/account-oauth.d.ts +9 -3
  18. package/dist/src/handlers/account-oauth.js +197 -250
  19. package/dist/src/handlers/account.d.ts +13 -4
  20. package/dist/src/handlers/account.js +26 -39
  21. package/dist/src/handlers/address.d.ts +11 -3
  22. package/dist/src/handlers/address.js +11 -19
  23. package/dist/src/handlers/calendar-event.js +18 -26
  24. package/dist/src/handlers/calendar-feed.d.ts +2 -2
  25. package/dist/src/handlers/calendar-feed.js +24 -32
  26. package/dist/src/handlers/calendar-suggestion.js +18 -26
  27. package/dist/src/handlers/calendar.d.ts +1 -1
  28. package/dist/src/handlers/calendar.js +15 -22
  29. package/dist/src/handlers/config.d.ts +16 -6
  30. package/dist/src/handlers/config.js +23 -35
  31. package/dist/src/handlers/filter.d.ts +16 -3
  32. package/dist/src/handlers/filter.js +17 -32
  33. package/dist/src/handlers/folder-role.js +3 -6
  34. package/dist/src/handlers/index.d.ts +187 -2
  35. package/dist/src/handlers/index.js +6 -4
  36. package/dist/src/handlers/label.js +15 -22
  37. package/dist/src/handlers/mailbox.d.ts +20 -4
  38. package/dist/src/handlers/mailbox.js +18 -26
  39. package/dist/src/handlers/me.d.ts +10 -2
  40. package/dist/src/handlers/me.js +26 -47
  41. package/dist/src/handlers/message.d.ts +78 -6
  42. package/dist/src/handlers/message.js +34 -46
  43. package/dist/src/handlers/organize.d.ts +10 -4
  44. package/dist/src/handlers/organize.js +11 -20
  45. package/dist/src/handlers/outbox-attachment.d.ts +6 -5
  46. package/dist/src/handlers/outbox-attachment.js +11 -15
  47. package/dist/src/handlers/outbox.d.ts +25 -4
  48. package/dist/src/handlers/outbox.js +27 -35
  49. package/dist/src/handlers/sync.d.ts +19 -6
  50. package/dist/src/handlers/sync.js +13 -22
  51. package/dist/src/handlers/system-update.js +14 -27
  52. package/dist/src/handlers/thread.js +9 -14
  53. package/dist/src/handlers/unified-threads.js +3 -6
  54. package/dist/src/index.d.ts +4 -1
  55. package/dist/src/index.js +18 -2
  56. package/dist/src/jwt-auth.d.ts +9 -12
  57. package/dist/src/jwt-auth.js +19 -71
  58. package/dist/src/lambda.d.ts +13 -0
  59. package/dist/src/lambda.js +33 -0
  60. package/dist/src/request.d.ts +8 -2
  61. package/dist/src/request.js +16 -6
  62. package/dist/src/response.d.ts +20 -3
  63. package/dist/src/response.js +15 -22
  64. package/dist/src/serve.d.ts +31 -0
  65. package/dist/src/serve.js +117 -0
  66. package/dist/src/service/account-deletion.d.ts +2 -4
  67. package/dist/src/service/account-deletion.js +3 -9
  68. package/dist/src/service/compose-sqlite.d.ts +2 -1
  69. package/dist/src/service/compose-sqlite.js +5 -3
  70. package/dist/src/service/create-remit-client.d.ts +25 -11
  71. package/dist/src/service/create-remit-client.js +97 -116
  72. package/dist/src/service/data-client.js +3 -5
  73. package/dist/src/service/export-identity.d.ts +2 -1
  74. package/dist/src/service/export-identity.js +4 -6
  75. package/dist/src/service/fire-and-forget.js +1 -2
  76. package/dist/src/service/organize.d.ts +7 -10
  77. package/dist/src/service/organize.js +10 -27
  78. package/dist/src/service/queues.d.ts +12 -0
  79. package/dist/src/service/queues.js +1 -0
  80. package/dist/src/service/shared-deps.d.ts +6 -0
  81. package/dist/src/service/shared-deps.js +26 -0
  82. package/dist/src/service/trigger-sync.d.ts +11 -5
  83. package/dist/src/service/trigger-sync.js +10 -18
  84. package/dist/src/types.d.ts +24 -1
  85. package/package.json +24 -8
  86. package/dist/src/data-backend.d.ts +0 -12
  87. package/dist/src/data-backend.js +0 -12
  88. package/dist/src/request-context.d.ts +0 -9
  89. package/dist/src/request-context.js +0 -22
  90. package/dist/src/service/sqs.d.ts +0 -1
  91. package/dist/src/service/sqs.js +0 -3
@@ -1,7 +1,7 @@
1
1
  import type { RenameMailboxInput } from "@remit/api-openapi-types";
2
2
  import type { IAccountSettingRepository, MailboxItem } from "@remit/data-ports";
3
3
  import { type FolderPathDeps } from "@remit/mailbox-service";
4
- import type { MailboxDetailOperationIds, MailboxOperationIds, OperationHandler, TrashOperationIds } from "../types.js";
4
+ import type { HandlerContext } from "../types.js";
5
5
  /**
6
6
  * The mute flag and the display-name override are user preferences that live
7
7
  * in per-mailbox AccountSetting rows (RFC 032), not on the Mailbox entity.
@@ -18,6 +18,22 @@ export type MailboxPatchClient = FolderPathDeps & {
18
18
  accountSetting: Pick<IAccountSettingRepository, "get" | "upsert" | "delete">;
19
19
  };
20
20
  export declare const applyMailboxPatch: (client: MailboxPatchClient, accountConfigId: string, mailboxId: string, accountId: string, body: RenameMailboxInput) => Promise<MailboxItem>;
21
- export declare const MailboxOperations: Record<MailboxOperationIds, OperationHandler<MailboxOperationIds>>;
22
- export declare const MailboxDetailOperations: Record<MailboxDetailOperationIds, OperationHandler<MailboxDetailOperationIds>>;
23
- export declare const TrashOperations: Record<TrashOperationIds, OperationHandler<TrashOperationIds>>;
21
+ export declare const MailboxOperations: {
22
+ MailboxOperations_listMailboxes: (context: import("openapi-backend").Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext) => Promise<{
23
+ items: import("@remit/api-openapi-types").Components.Schemas.MailboxResponse[];
24
+ continuationToken: string | undefined;
25
+ }>;
26
+ MailboxOperations_createMailbox: (context: import("openapi-backend").Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext<"sync">) => Promise<import("@remit/api-openapi-types").Components.Schemas.MailboxResponse>;
27
+ };
28
+ export declare const MailboxDetailOperations: {
29
+ MailboxDetailOperations_getMailbox: (context: import("openapi-backend").Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext) => Promise<import("@remit/api-openapi-types").Components.Schemas.MailboxResponse>;
30
+ MailboxDetailOperations_renameMailbox: (context: import("openapi-backend").Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext<"sync">) => Promise<import("@remit/api-openapi-types").Components.Schemas.MailboxResponse>;
31
+ MailboxDetailOperations_deleteMailbox: (context: import("openapi-backend").Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext<"sync">) => Promise<{
32
+ statusCode: number;
33
+ }>;
34
+ };
35
+ export declare const TrashOperations: {
36
+ TrashOperations_emptyTrash: (context: import("openapi-backend").Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext<"sync">) => Promise<{
37
+ deletedCount: number;
38
+ }>;
39
+ };
@@ -1,9 +1,7 @@
1
1
  import { BadRequestError, ConflictError } from "@remit/data-ports/errors";
2
2
  import { MessageSystemFlag } from "@remit/domain-enums";
3
3
  import { changeFolderPath, createFolder, folderInAccount, folderInFlight, ownedAccount, ownedFolder, } from "@remit/mailbox-service";
4
- import { getAccountConfigIdFromEvent } from "../auth.js";
5
4
  import { applyPendingMoveCountPrediction, } from "../derive/pendingMoveCounts.js";
6
- import { getClient } from "../service/data-client.js";
7
5
  import { unwrap } from "../service/refusal.js";
8
6
  import { applyMailboxOverrideChanges, loadMailboxOverrides, loadMailboxOverridesForConfig, } from "./account-overrides.js";
9
7
  import { assertNoBindings, loadMailboxBindings } from "./mailbox-bindings.js";
@@ -100,12 +98,11 @@ const toMailboxResponse = (mailbox, overrides = {}) => ({
100
98
  updatedAt: mailbox.updatedAt,
101
99
  });
102
100
  export const MailboxOperations = {
103
- MailboxOperations_listMailboxes: async (context, ...args) => {
104
- const event = args[0];
105
- const accountConfigId = getAccountConfigIdFromEvent(event);
101
+ MailboxOperations_listMailboxes: async (context, ctx) => {
102
+ const accountConfigId = ctx.identity.accountConfigId;
106
103
  const { accountId } = context.request.params;
107
104
  const { continuationToken } = context.request.query;
108
- const client = await getClient();
105
+ const client = ctx.client;
109
106
  const account = await client.account.get(accountId);
110
107
  unwrap(ownedAccount(account, accountConfigId, "read"));
111
108
  const result = await client.mailbox.listByAccount(accountId, {
@@ -129,13 +126,12 @@ export const MailboxOperations = {
129
126
  continuationToken: result.continuationToken,
130
127
  };
131
128
  },
132
- MailboxOperations_createMailbox: async (context, ...args) => {
133
- const event = args[0];
134
- const accountConfigId = getAccountConfigIdFromEvent(event);
129
+ MailboxOperations_createMailbox: async (context, ctx) => {
130
+ const accountConfigId = ctx.identity.accountConfigId;
135
131
  const { accountId } = context.request.params;
136
132
  const { namespaceType, fullPath } = context.request
137
133
  .requestBody;
138
- const created = await createFolder(await getClient(), accountConfigId, {
134
+ const created = await createFolder(ctx.client, accountConfigId, {
139
135
  accountId,
140
136
  namespaceType,
141
137
  fullPath,
@@ -148,11 +144,10 @@ export const MailboxOperations = {
148
144
  },
149
145
  };
150
146
  export const MailboxDetailOperations = {
151
- MailboxDetailOperations_getMailbox: async (context, ...args) => {
152
- const event = args[0];
153
- const accountConfigId = getAccountConfigIdFromEvent(event);
147
+ MailboxDetailOperations_getMailbox: async (context, ctx) => {
148
+ const accountConfigId = ctx.identity.accountConfigId;
154
149
  const { accountId, mailboxId } = context.request.params;
155
- const client = await getClient();
150
+ const client = ctx.client;
156
151
  const account = await client.account.get(accountId);
157
152
  unwrap(ownedAccount(account, accountConfigId, "read"));
158
153
  const mailbox = await client.mailbox.get(accountId, mailboxId);
@@ -166,21 +161,19 @@ export const MailboxDetailOperations = {
166
161
  const adjusted = applyPendingMoveCountPrediction([mailbox], pendingMoves, pendingUnseenFlagPushes)[0] ?? mailbox;
167
162
  return toMailboxResponse(adjusted, overrides);
168
163
  },
169
- MailboxDetailOperations_renameMailbox: async (context, ...args) => {
170
- const event = args[0];
171
- const accountConfigId = getAccountConfigIdFromEvent(event);
164
+ MailboxDetailOperations_renameMailbox: async (context, ctx) => {
165
+ const accountConfigId = ctx.identity.accountConfigId;
172
166
  const { accountId, mailboxId } = context.request.params;
173
167
  const body = context.request.requestBody;
174
- const client = await getClient();
168
+ const client = ctx.client;
175
169
  const mailbox = await applyMailboxPatch(client, accountConfigId, mailboxId, accountId, body);
176
170
  const overrides = await loadMailboxOverrides(client.accountSetting, accountConfigId, mailboxId);
177
171
  return toMailboxResponse(mailbox, overrides);
178
172
  },
179
- MailboxDetailOperations_deleteMailbox: async (context, ...args) => {
180
- const event = args[0];
181
- const accountConfigId = getAccountConfigIdFromEvent(event);
173
+ MailboxDetailOperations_deleteMailbox: async (context, ctx) => {
174
+ const accountConfigId = ctx.identity.accountConfigId;
182
175
  const { accountId, mailboxId } = context.request.params;
183
- const client = await getClient();
176
+ const client = ctx.client;
184
177
  const account = await client.account.get(accountId);
185
178
  unwrap(ownedAccount(account, accountConfigId, "act"));
186
179
  const mailbox = await client.mailbox.get(accountId, mailboxId);
@@ -203,11 +196,10 @@ export const MailboxDetailOperations = {
203
196
  },
204
197
  };
205
198
  export const TrashOperations = {
206
- TrashOperations_emptyTrash: async (context, ...args) => {
207
- const event = args[0];
208
- const accountConfigId = getAccountConfigIdFromEvent(event);
199
+ TrashOperations_emptyTrash: async (context, ctx) => {
200
+ const accountConfigId = ctx.identity.accountConfigId;
209
201
  const { accountId } = context.request.params;
210
- const client = await getClient();
202
+ const client = ctx.client;
211
203
  const account = await client.account.get(accountId);
212
204
  unwrap(ownedAccount(account, accountConfigId, "act"));
213
205
  // The service resolves Trash, marks the rows and counts them, all off one
@@ -1,2 +1,10 @@
1
- import type { MeOperationIds, OperationHandler } from "../types.js";
2
- export declare const MeOperations: Record<MeOperationIds, OperationHandler<MeOperationIds>>;
1
+ import type { AccountExportRequestResponse, CreateExportResponse, DeleteAccountConfigResponse, QuarantineListResponse, VipSuggestionsResponse } from "@remit/api-openapi-types";
2
+ import type { Context } from "openapi-backend";
3
+ import type { HandlerContext } from "../types.js";
4
+ export declare const MeOperations: {
5
+ MeOperations_listVipSuggestions: (_context: Context, ctx: HandlerContext) => Promise<VipSuggestionsResponse>;
6
+ MeOperations_listQuarantine: (_context: Context, ctx: HandlerContext) => Promise<QuarantineListResponse>;
7
+ MeOperations_deleteMe: (context: Context, ctx: HandlerContext<"accountFanout">) => Promise<DeleteAccountConfigResponse>;
8
+ MeOperations_createExport: (_context: Context, ctx: HandlerContext<"accountFanout">) => Promise<CreateExportResponse>;
9
+ MeOperations_getExport: (context: Context, ctx: HandlerContext) => Promise<AccountExportRequestResponse>;
10
+ };
@@ -1,26 +1,19 @@
1
- import { SendMessageCommand } from "@aws-sdk/client-sqs";
2
1
  import { BadRequestError, NotFoundError } from "@remit/data-ports/errors";
3
2
  import { SYNC_WARNING_SURFACE_ATTEMPTS } from "@remit/data-ports/sync-warning-state";
4
3
  import { logger } from "@remit/logger";
5
- import { env } from "expect-env";
6
- import { getAccountConfigIdFromEvent, getSubFromEvent } from "../auth.js";
7
- import { getClient } from "../service/data-client.js";
8
- import { sqsClient } from "../service/sqs.js";
9
4
  import { toVipSuggestionEntry } from "./vip-suggestions.js";
10
5
  const DOWNLOAD_URL_TTL_SECONDS = 60 * 60;
11
6
  const SURFACED_WARNING_CAP = 200;
12
7
  export const MeOperations = {
13
- MeOperations_listVipSuggestions: async (_context, ...args) => {
14
- const event = args[0];
15
- const accountConfigId = getAccountConfigIdFromEvent(event);
16
- const { address } = await getClient();
8
+ MeOperations_listVipSuggestions: async (_context, ctx) => {
9
+ const accountConfigId = ctx.identity.accountConfigId;
10
+ const { address } = ctx.client;
17
11
  const items = await address.listSuggestedVips({ accountConfigId });
18
12
  return { suggestions: items.map(toVipSuggestionEntry) };
19
13
  },
20
- MeOperations_listQuarantine: async (_context, ...args) => {
21
- const event = args[0];
22
- const accountConfigId = getAccountConfigIdFromEvent(event);
23
- const { quarantine, syncWarning } = await getClient();
14
+ MeOperations_listQuarantine: async (_context, ctx) => {
15
+ const accountConfigId = ctx.identity.accountConfigId;
16
+ const { quarantine, syncWarning } = ctx.client;
24
17
  const [entries, warnings] = await Promise.all([
25
18
  quarantine.listByAccountConfigId(accountConfigId),
26
19
  syncWarning.listSurfacedByAccountConfigId(accountConfigId, SYNC_WARNING_SURFACE_ATTEMPTS, SURFACED_WARNING_CAP),
@@ -31,21 +24,18 @@ export const MeOperations = {
31
24
  warningsTotal: warnings.total,
32
25
  };
33
26
  },
34
- MeOperations_deleteMe: async (_context, ...args) => {
35
- const event = args[0];
36
- const accountConfigId = getAccountConfigIdFromEvent(event);
37
- const { confirmEmail } = JSON.parse(event.body ?? "{}");
38
- const callerEmail = event.requestContext?.authorizer?.claims?.email;
27
+ MeOperations_deleteMe: async (context, ctx) => {
28
+ const accountConfigId = ctx.identity.accountConfigId;
29
+ const { confirmEmail } = context.request
30
+ .requestBody;
31
+ const callerEmail = ctx.identity.email;
39
32
  if (typeof confirmEmail !== "string" || confirmEmail.trim() === "") {
40
33
  throw new BadRequestError("confirmEmail is required");
41
34
  }
42
- if (!callerEmail) {
43
- throw new BadRequestError("Account email claim missing from token");
44
- }
45
35
  if (confirmEmail.toLowerCase() !== callerEmail.toLowerCase()) {
46
36
  throw new BadRequestError("confirmEmail does not match your account email");
47
37
  }
48
- const { accountConfig } = await getClient();
38
+ const { accountConfig } = ctx.client;
49
39
  try {
50
40
  await accountConfig.update(accountConfigId, {
51
41
  state: "deleting",
@@ -64,14 +54,9 @@ export const MeOperations = {
64
54
  }
65
55
  throw err;
66
56
  }
67
- const queueUrl = env.SQS_QUEUE_URL_ACCOUNT_FANOUT;
68
- await sqsClient.send(new SendMessageCommand({
69
- QueueUrl: queueUrl,
70
- MessageBody: JSON.stringify({
71
- type: "AccountDelete",
72
- accountConfigId,
73
- }),
74
- }));
57
+ await ctx.client.queues.accountFanout.send({
58
+ body: { type: "AccountDelete", accountConfigId },
59
+ });
75
60
  // biome-ignore lint/plugin/no-logger-info: account deletion is an audit-grade signal
76
61
  logger.info({ accountConfigId }, "Account deletion initiated");
77
62
  return {
@@ -79,27 +64,22 @@ export const MeOperations = {
79
64
  message: "Account deletion initiated",
80
65
  };
81
66
  },
82
- MeOperations_createExport: async (_context, ...args) => {
83
- const event = args[0];
84
- const accountConfigId = getAccountConfigIdFromEvent(event);
85
- const userId = getSubFromEvent(event);
86
- if (!userId) {
87
- throw new Error("Missing Cognito `sub`: cannot attribute an export request to a user");
88
- }
89
- const { accountExportRequest } = await getClient();
67
+ MeOperations_createExport: async (_context, ctx) => {
68
+ const accountConfigId = ctx.identity.accountConfigId;
69
+ const userId = ctx.identity.sub;
70
+ const { accountExportRequest } = ctx.client;
90
71
  const exportRequest = await accountExportRequest.create({
91
72
  accountConfigId,
92
73
  userId,
93
74
  state: "Pending",
94
75
  });
95
- await sqsClient.send(new SendMessageCommand({
96
- QueueUrl: env.SQS_QUEUE_URL_ACCOUNT_FANOUT,
97
- MessageBody: JSON.stringify({
76
+ await ctx.client.queues.accountFanout.send({
77
+ body: {
98
78
  type: "AccountExport",
99
79
  accountConfigId,
100
80
  accountExportRequestId: exportRequest.accountExportRequestId,
101
- }),
102
- }));
81
+ },
82
+ });
103
83
  // biome-ignore lint/plugin/no-logger-info: account export is an audit-grade signal
104
84
  logger.info({
105
85
  accountConfigId,
@@ -110,11 +90,10 @@ export const MeOperations = {
110
90
  exportId: exportRequest.accountExportRequestId,
111
91
  };
112
92
  },
113
- MeOperations_getExport: async (context, ...args) => {
114
- const event = args[0];
115
- const accountConfigId = getAccountConfigIdFromEvent(event);
93
+ MeOperations_getExport: async (context, ctx) => {
94
+ const accountConfigId = ctx.identity.accountConfigId;
116
95
  const exportId = context.request.params.exportId;
117
- const { accountExportRequest, storage } = await getClient();
96
+ const { accountExportRequest, storage } = ctx.client;
118
97
  const exportRequest = await accountExportRequest.get(exportId);
119
98
  if (exportRequest.accountConfigId !== accountConfigId) {
120
99
  throw new NotFoundError(`Export not found: ${exportId}`);
@@ -1,6 +1,7 @@
1
1
  import type { BodyPartResponse } from "@remit/api-openapi-types";
2
+ import type { Context } from "openapi-backend";
2
3
  import { type ContentSigner } from "../derive/contentSignature.js";
3
- import type { MessageBulkOperationIds, MessageOperationIds, OperationHandler } from "../types.js";
4
+ import type { HandlerContext } from "../types.js";
4
5
  export declare const isStorageNotFoundError: (error: unknown) => boolean;
5
6
  /**
6
7
  * Decode the raw `.eml` bytes into a string for inspection. RFC822/MIME
@@ -26,9 +27,7 @@ export declare const extractAccountIdsFromBodyKey: (uri: string) => {
26
27
  * populating `contentUrl` from the CloudFront distribution domain. Pure
27
28
  * function so the URL-construction contract can be pinned in tests without
28
29
  * standing up the full handler. The contract requires every `contentUrl`
29
- * to be a real URL (#299) — `getContentDeliveryDomain` throws when the
30
- * Lambda env is missing the domain, and this function expects a non-empty
31
- * string from the caller.
30
+ * to be a real URL (#299), so the API refuses to start without the domain.
32
31
  */
33
32
  export interface BodyPartLike {
34
33
  bodyPartId: string;
@@ -80,5 +79,78 @@ export declare const GENERIC_FAILURE_REASON = "This message could not be process
80
79
  * regardless of batch size.
81
80
  */
82
81
  export declare const settleSpamReportBulk: (messageIds: string[], run: (messageId: string) => Promise<void>) => Promise<SpamReportBulkOutcome>;
83
- export declare const MessageOperations: Record<MessageOperationIds, OperationHandler<MessageOperationIds>>;
84
- export declare const MessageBulkOperations: Record<MessageBulkOperationIds, OperationHandler<MessageBulkOperationIds>>;
82
+ export declare const MessageOperations: {
83
+ MessageOperations_describeMessage: (context: Context, ctx: HandlerContext<"sync" | "messageManagement" | "body">) => Promise<{
84
+ message: {
85
+ spamReport?: {
86
+ reportedAt: number;
87
+ } | undefined;
88
+ labels?: {
89
+ labelId: string;
90
+ name: string;
91
+ color: "Default" | "Red" | "Orange" | "Yellow" | "Green" | "Teal" | "Blue" | "Purple" | "Gray";
92
+ }[] | undefined;
93
+ autoMoved?: import("@remit/api-openapi-types").Components.Schemas.AutoMovedInfo | undefined;
94
+ messageId: string;
95
+ mailboxId: string;
96
+ uid: number;
97
+ rfc822Size: number;
98
+ internalDate: number;
99
+ messageIdHeader: string | undefined;
100
+ authenticity: {
101
+ fromDomain: string;
102
+ dkimMismatch: boolean;
103
+ dkimDomain?: string | undefined;
104
+ displayNameCorrespondence?: "Corresponds" | "Lookalike" | "ForeignAddress" | "Unrelated" | "NoClaim" | undefined;
105
+ offDomainLinkDomains?: string[] | undefined;
106
+ signerDomain?: string | undefined;
107
+ envelopeDomain?: string | undefined;
108
+ signerMessageCount?: number | undefined;
109
+ signerFirstSeenAt?: number | undefined;
110
+ } | undefined;
111
+ status: "active" | "deleting" | "deleted" | "moving";
112
+ syncStatus: "failed" | "synced" | "pending" | "abandoned";
113
+ abandonedMutation: "none" | "move" | "delete" | "copy";
114
+ };
115
+ envelope: import("@remit/api-openapi-types").Components.Schemas.EnvelopeResponse;
116
+ flags: string[];
117
+ bodyParts: import("@remit/api-openapi-types").Components.Schemas.BodyPartResponse[];
118
+ references: {
119
+ messageIdValue: string;
120
+ referenceType: string;
121
+ referenceOrder: number;
122
+ }[];
123
+ }>;
124
+ MessageOperations_getRawMessage: (context: Context, ctx: HandlerContext<"sync" | "messageManagement">) => Promise<{
125
+ raw: string;
126
+ }>;
127
+ MessageOperations_updateMessageFlags: (context: Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext<"sync">) => Promise<{
128
+ messageId: string;
129
+ isRead: boolean;
130
+ isStarred: boolean;
131
+ }>;
132
+ };
133
+ export declare const MessageBulkOperations: {
134
+ MessageBulkOperations_updateFlags: (context: Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext<"sync">) => Promise<{
135
+ successCount: number;
136
+ failureCount: number;
137
+ }>;
138
+ MessageBulkOperations_deleteMessages: (context: Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext<"sync">) => Promise<{
139
+ successCount: number;
140
+ failureCount: number;
141
+ }>;
142
+ MessageBulkOperations_moveMessages: (context: Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext<"sync">) => Promise<{
143
+ successCount: number;
144
+ failureCount: number;
145
+ }>;
146
+ MessageBulkOperations_copyMessages: (context: Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext<"sync">) => Promise<{
147
+ successCount: number;
148
+ failureCount: number;
149
+ }>;
150
+ MessageBulkOperations_updateMessageLabels: (context: Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext) => Promise<{
151
+ successCount: number;
152
+ failureCount: number;
153
+ }>;
154
+ MessageBulkOperations_reportSpam: (context: Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext<"sync">) => Promise<SpamReportBulkOutcome>;
155
+ MessageBulkOperations_notSpam: (context: Context<any, any, any, any, any, import("openapi-backend").Document>, ctx: HandlerContext<"sync">) => Promise<SpamReportBulkOutcome>;
156
+ };
@@ -4,12 +4,10 @@ import { ContentDisposition, MediaType, MessageCategory, SenderTrust, } from "@r
4
4
  import { logger } from "@remit/logger";
5
5
  import { guardConnectionCursor, isCursorRebuildNeeded, isMessageBodySyncBroken, MailboxCursorPausedError, MoveNotSettledError, materializeBodyParts, NoJunkMailboxError, ownedMessages, resolveFolderPlacement, settledFolder, } from "@remit/mailbox-service";
6
6
  import { isStorageNotFoundError as isStorageNotFoundErrorFromService, parseStorageUri, } from "@remit/storage-service";
7
- import { getAccountConfigIdFromEvent, getSubFromEvent } from "../auth.js";
8
7
  import { deriveAutoMoved } from "../derive/autoMoved.js";
9
8
  import { getContentSigner, } from "../derive/contentSignature.js";
10
- import { buildContentUrl, getContentDeliveryDomain, } from "../derive/contentUrl.js";
9
+ import { buildContentUrl } from "../derive/contentUrl.js";
11
10
  import { deriveSenderTrust } from "../derive/senderTrust.js";
12
- import { getClient } from "../service/data-client.js";
13
11
  import { unwrap } from "../service/refusal.js";
14
12
  export const isStorageNotFoundError = (error) => isStorageNotFoundErrorFromService(error);
15
13
  /**
@@ -144,11 +142,10 @@ export const settleSpamReportBulk = async (messageIds, run) => {
144
142
  };
145
143
  };
146
144
  export const MessageOperations = {
147
- MessageOperations_describeMessage: async (context, ...args) => {
148
- const event = args[0];
149
- const accountConfigId = getAccountConfigIdFromEvent(event);
145
+ MessageOperations_describeMessage: async (context, ctx) => {
146
+ const accountConfigId = ctx.identity.accountConfigId;
150
147
  const { messageId } = context.request.params;
151
- const client = await getClient();
148
+ const client = ctx.client;
152
149
  const ownedAccountId = unwrap(await ownedMessages(client, [messageId], accountConfigId, "read"));
153
150
  const description = await client.message.describe(messageId);
154
151
  const message = description.message[0];
@@ -335,11 +332,11 @@ export const MessageOperations = {
335
332
  logger,
336
333
  });
337
334
  const bodyParts = buildBodyPartResponses(bodyPartRows, {
338
- contentDeliveryDomain: getContentDeliveryDomain(),
335
+ contentDeliveryDomain: ctx.config.contentDeliveryDomain,
339
336
  accountConfigId,
340
337
  accountId,
341
338
  messageId,
342
- sign: getContentSigner(),
339
+ sign: getContentSigner(ctx.config),
343
340
  });
344
341
  const references = description.messageReference.map((ref) => ({
345
342
  messageIdValue: ref.messageIdValue,
@@ -354,11 +351,10 @@ export const MessageOperations = {
354
351
  references,
355
352
  };
356
353
  },
357
- MessageOperations_getRawMessage: async (context, ...args) => {
358
- const event = args[0];
359
- const accountConfigId = getAccountConfigIdFromEvent(event);
354
+ MessageOperations_getRawMessage: async (context, ctx) => {
355
+ const accountConfigId = ctx.identity.accountConfigId;
360
356
  const { messageId } = context.request.params;
361
- const client = await getClient();
357
+ const client = ctx.client;
362
358
  // Cross-tenant ownership guard. Returning raw RFC822 bytes is sensitive,
363
359
  // so resolve the owning account and prove the caller owns it BEFORE any
364
360
  // storage read or backfill. `read` mode throws NotFoundError (→ 404) on a
@@ -421,12 +417,11 @@ export const MessageOperations = {
421
417
  const body = await client.storage.retrieve(stored.bodyStorageKey);
422
418
  return { raw: decodeRawEml(body) };
423
419
  },
424
- MessageOperations_updateMessageFlags: async (context, ...args) => {
425
- const event = args[0];
426
- const accountConfigId = getAccountConfigIdFromEvent(event);
420
+ MessageOperations_updateMessageFlags: async (context, ctx) => {
421
+ const accountConfigId = ctx.identity.accountConfigId;
427
422
  const { messageId } = context.request.params;
428
423
  const { isRead, isStarred, starColor } = context.request.requestBody;
429
- const client = await getClient();
424
+ const client = ctx.client;
430
425
  const accountId = unwrap(await ownedMessages(client, [messageId], accountConfigId, "act"));
431
426
  // FlagQueueService handles: MessageFlag + ThreadMessage updates + SQS event
432
427
  const result = await client.flagQueue.updateFlags(accountConfigId, messageId, accountId, {
@@ -442,15 +437,14 @@ export const MessageOperations = {
442
437
  },
443
438
  };
444
439
  export const MessageBulkOperations = {
445
- MessageBulkOperations_updateFlags: async (context, ...args) => {
446
- const event = args[0];
447
- const accountConfigId = getAccountConfigIdFromEvent(event);
440
+ MessageBulkOperations_updateFlags: async (context, ctx) => {
441
+ const accountConfigId = ctx.identity.accountConfigId;
448
442
  const { messageIds, isRead, isStarred, starColor } = context.request
449
443
  .requestBody;
450
444
  if (messageIds.length === 0) {
451
445
  return { successCount: 0, failureCount: 0 };
452
446
  }
453
- const client = await getClient();
447
+ const client = ctx.client;
454
448
  const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
455
449
  // FlagQueueService handles: MessageFlag + ThreadMessage updates + SQS events
456
450
  // Process each message individually (same pattern as moveMessages)
@@ -466,14 +460,13 @@ export const MessageBulkOperations = {
466
460
  failureCount: 0,
467
461
  };
468
462
  },
469
- MessageBulkOperations_deleteMessages: async (context, ...args) => {
470
- const event = args[0];
471
- const accountConfigId = getAccountConfigIdFromEvent(event);
463
+ MessageBulkOperations_deleteMessages: async (context, ctx) => {
464
+ const accountConfigId = ctx.identity.accountConfigId;
472
465
  const { messageIds, permanent } = context.request.requestBody;
473
466
  if (messageIds.length === 0) {
474
467
  return { successCount: 0, failureCount: 0 };
475
468
  }
476
- const client = await getClient();
469
+ const client = ctx.client;
477
470
  const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
478
471
  // MessageMoveService handles: Message + ThreadMessage updates + SQS events
479
472
  const { refusedMessageIds } = await client.messageMove.deleteMessages(accountConfigId, messageIds, accountId, {
@@ -489,15 +482,14 @@ export const MessageBulkOperations = {
489
482
  failureCount: refusedMessageIds.length,
490
483
  };
491
484
  },
492
- MessageBulkOperations_moveMessages: async (context, ...args) => {
493
- const event = args[0];
494
- const accountConfigId = getAccountConfigIdFromEvent(event);
485
+ MessageBulkOperations_moveMessages: async (context, ctx) => {
486
+ const accountConfigId = ctx.identity.accountConfigId;
495
487
  const { messageIds, destinationMailboxId } = context.request
496
488
  .requestBody;
497
489
  if (messageIds.length === 0) {
498
490
  return { successCount: 0, failureCount: 0 };
499
491
  }
500
- const client = await getClient();
492
+ const client = ctx.client;
501
493
  const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
502
494
  // Destination must belong to the same (caller-owned) account, and must be
503
495
  // a folder the mail server has settled: the row records the destination
@@ -515,15 +507,14 @@ export const MessageBulkOperations = {
515
507
  failureCount: 0,
516
508
  };
517
509
  },
518
- MessageBulkOperations_copyMessages: async (context, ...args) => {
519
- const event = args[0];
520
- const accountConfigId = getAccountConfigIdFromEvent(event);
510
+ MessageBulkOperations_copyMessages: async (context, ctx) => {
511
+ const accountConfigId = ctx.identity.accountConfigId;
521
512
  const { messageIds, destinationMailboxId } = context.request
522
513
  .requestBody;
523
514
  if (messageIds.length === 0) {
524
515
  return { successCount: 0, failureCount: 0 };
525
516
  }
526
- const client = await getClient();
517
+ const client = ctx.client;
527
518
  const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
528
519
  // Destination must belong to the same (caller-owned) account, and must be
529
520
  // a folder the mail server has settled: the row records the destination
@@ -541,14 +532,13 @@ export const MessageBulkOperations = {
541
532
  failureCount: 0,
542
533
  };
543
534
  },
544
- MessageBulkOperations_updateMessageLabels: async (context, ...args) => {
545
- const event = args[0];
546
- const accountConfigId = getAccountConfigIdFromEvent(event);
535
+ MessageBulkOperations_updateMessageLabels: async (context, ctx) => {
536
+ const accountConfigId = ctx.identity.accountConfigId;
547
537
  const { messageIds, labelId, action } = context.request.requestBody;
548
538
  if (messageIds.length === 0) {
549
539
  return { successCount: 0, failureCount: 0 };
550
540
  }
551
- const client = await getClient();
541
+ const client = ctx.client;
552
542
  unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
553
543
  // Scoped by accountConfigId (RFC 030) — a foreign labelId 404s here rather
554
544
  // than silently applying to messages under someone else's label.
@@ -574,17 +564,16 @@ export const MessageBulkOperations = {
574
564
  failureCount: 0,
575
565
  };
576
566
  },
577
- MessageBulkOperations_reportSpam: async (context, ...args) => {
578
- const event = args[0];
579
- const accountConfigId = getAccountConfigIdFromEvent(event);
567
+ MessageBulkOperations_reportSpam: async (context, ctx) => {
568
+ const accountConfigId = ctx.identity.accountConfigId;
580
569
  const { messageIds: requestedIds } = context.request.requestBody;
581
570
  const messageIds = [...new Set(requestedIds)];
582
571
  if (messageIds.length === 0) {
583
572
  return { successCount: 0, failureCount: 0 };
584
573
  }
585
- const client = await getClient();
574
+ const client = ctx.client;
586
575
  const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
587
- const setBy = getSubFromEvent(event) ?? accountConfigId;
576
+ const setBy = ctx.identity.sub;
588
577
  return settleSpamReportBulk(messageIds, (messageId) => client.spamReport.reportSpam({
589
578
  accountConfigId,
590
579
  accountId,
@@ -592,15 +581,14 @@ export const MessageBulkOperations = {
592
581
  setBy,
593
582
  }));
594
583
  },
595
- MessageBulkOperations_notSpam: async (context, ...args) => {
596
- const event = args[0];
597
- const accountConfigId = getAccountConfigIdFromEvent(event);
584
+ MessageBulkOperations_notSpam: async (context, ctx) => {
585
+ const accountConfigId = ctx.identity.accountConfigId;
598
586
  const { messageIds: requestedIds } = context.request.requestBody;
599
587
  const messageIds = [...new Set(requestedIds)];
600
588
  if (messageIds.length === 0) {
601
589
  return { successCount: 0, failureCount: 0 };
602
590
  }
603
- const client = await getClient();
591
+ const client = ctx.client;
604
592
  const accountId = unwrap(await ownedMessages(client, messageIds, accountConfigId, "act"));
605
593
  return settleSpamReportBulk(messageIds, (messageId) => client.spamReport.notSpam({ accountConfigId, accountId, messageId }));
606
594
  },
@@ -1,10 +1,16 @@
1
- import type { OrganizeInput } from "@remit/api-openapi-types";
1
+ import type { CreateOrganizeJobResponse, OrganizeInput, OrganizeJobResponse, OrganizePreviewResponse } from "@remit/api-openapi-types";
2
+ import type { Context } from "openapi-backend";
2
3
  import { type OrganizePredicate } from "../service/organize.js";
3
- import type { OperationHandler, OrganizeJobDetailOperationIds, OrganizeOperationIds } from "../types.js";
4
+ import type { HandlerContext } from "../types.js";
4
5
  /**
5
6
  * Normalize the request body into the flattened predicate the job row and the
6
7
  * matcher share.
7
8
  */
8
9
  export declare const predicateFromInput: (input: OrganizeInput) => OrganizePredicate;
9
- export declare const OrganizeOperations: Record<OrganizeOperationIds, OperationHandler<OrganizeOperationIds>>;
10
- export declare const OrganizeJobDetailOperations: Record<OrganizeJobDetailOperationIds, OperationHandler<OrganizeJobDetailOperationIds>>;
10
+ export declare const OrganizeOperations: {
11
+ OrganizeOperations_createOrganizeJob: (context: Context, ctx: HandlerContext<"accountFanout">) => Promise<CreateOrganizeJobResponse>;
12
+ OrganizeOperations_previewOrganize: (context: Context, ctx: HandlerContext) => Promise<OrganizePreviewResponse>;
13
+ };
14
+ export declare const OrganizeJobDetailOperations: {
15
+ OrganizeJobDetailOperations_getOrganizeJob: (context: Context, ctx: HandlerContext) => Promise<OrganizeJobResponse>;
16
+ };