@remit/backend 0.0.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.
Files changed (87) hide show
  1. package/.env.test +7 -0
  2. package/dev-server/content-auth.test.ts +113 -0
  3. package/dev-server/content-auth.ts +54 -0
  4. package/dev-server/content-handler.test.ts +105 -0
  5. package/dev-server/content-handler.ts +79 -0
  6. package/dev-server/content-path.test.ts +37 -0
  7. package/dev-server/content-path.ts +27 -0
  8. package/dev-server/cors.test.ts +48 -0
  9. package/dev-server/cors.ts +25 -0
  10. package/dev-server/lambda-helpers.ts +69 -0
  11. package/dev-server/server.ts +289 -0
  12. package/package.json +82 -0
  13. package/src/auth.ts +92 -0
  14. package/src/config/msoauth.ts +60 -0
  15. package/src/data-backend.test.ts +25 -0
  16. package/src/data-backend.ts +20 -0
  17. package/src/derive/autoMoved.ts +28 -0
  18. package/src/derive/contentSignature.test.ts +153 -0
  19. package/src/derive/contentSignature.ts +139 -0
  20. package/src/derive/contentUrl.test.ts +156 -0
  21. package/src/derive/contentUrl.ts +70 -0
  22. package/src/derive/enrichThreadRows.ts +155 -0
  23. package/src/derive/filterThreadCriteria.test.ts +119 -0
  24. package/src/derive/filterThreadCriteria.ts +60 -0
  25. package/src/derive/pendingMoveCounts.ts +90 -0
  26. package/src/derive/senderTrust.test.ts +67 -0
  27. package/src/derive/senderTrust.ts +23 -0
  28. package/src/error.ts +55 -0
  29. package/src/handlers/account-guards.ts +137 -0
  30. package/src/handlers/account-oauth.test.ts +383 -0
  31. package/src/handlers/account-oauth.ts +452 -0
  32. package/src/handlers/account-overrides.test.ts +69 -0
  33. package/src/handlers/account-overrides.ts +286 -0
  34. package/src/handlers/account-ownership.ts +25 -0
  35. package/src/handlers/account-signature.ts +129 -0
  36. package/src/handlers/account.ts +524 -0
  37. package/src/handlers/address.ts +136 -0
  38. package/src/handlers/config.test.ts +184 -0
  39. package/src/handlers/config.ts +219 -0
  40. package/src/handlers/ensure-account-config.ts +30 -0
  41. package/src/handlers/filter.ts +294 -0
  42. package/src/handlers/folder-role-appointments.test.ts +250 -0
  43. package/src/handlers/folder-role-appointments.ts +272 -0
  44. package/src/handlers/folder-role.ts +76 -0
  45. package/src/handlers/index.ts +48 -0
  46. package/src/handlers/mailbox.ts +411 -0
  47. package/src/handlers/me.ts +190 -0
  48. package/src/handlers/message.ts +886 -0
  49. package/src/handlers/organize.test.ts +43 -0
  50. package/src/handlers/organize.ts +196 -0
  51. package/src/handlers/outbox.ts +218 -0
  52. package/src/handlers/search.test.ts +182 -0
  53. package/src/handlers/search.ts +105 -0
  54. package/src/handlers/sync-progress.test.ts +158 -0
  55. package/src/handlers/sync-progress.ts +64 -0
  56. package/src/handlers/sync.test.ts +146 -0
  57. package/src/handlers/sync.ts +119 -0
  58. package/src/handlers/thread.ts +361 -0
  59. package/src/handlers/unified-threads.ts +196 -0
  60. package/src/handlers/vip-suggestions.ts +21 -0
  61. package/src/index.ts +170 -0
  62. package/src/json.ts +11 -0
  63. package/src/jwt-auth.test.ts +89 -0
  64. package/src/jwt-auth.ts +95 -0
  65. package/src/request-context.test.ts +61 -0
  66. package/src/request-context.ts +29 -0
  67. package/src/request.ts +10 -0
  68. package/src/response.test.ts +107 -0
  69. package/src/response.ts +80 -0
  70. package/src/service/compose-postgres.ts +72 -0
  71. package/src/service/compose-sqlite.ts +80 -0
  72. package/src/service/create-remit-client.ts +358 -0
  73. package/src/service/dynamodb.test.ts +100 -0
  74. package/src/service/dynamodb.ts +58 -0
  75. package/src/service/filter.ts +55 -0
  76. package/src/service/fire-and-forget.test.ts +126 -0
  77. package/src/service/fire-and-forget.ts +79 -0
  78. package/src/service/localhost-env-config.test.ts +40 -0
  79. package/src/service/organize.test.ts +384 -0
  80. package/src/service/organize.ts +354 -0
  81. package/src/service/semantic-capability.test.ts +64 -0
  82. package/src/service/semantic-capability.ts +62 -0
  83. package/src/service/sqs.ts +4 -0
  84. package/src/service/trigger-sync.test.ts +211 -0
  85. package/src/service/trigger-sync.ts +102 -0
  86. package/src/types.ts +161 -0
  87. package/tsconfig.json +7 -0
@@ -0,0 +1,102 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { SendMessageCommand, type SQSClient } from "@aws-sdk/client-sqs";
3
+
4
+ interface SyncMailboxesEvent {
5
+ type: "SYNC_MAILBOXES";
6
+ eventId: string;
7
+ timestamp: number;
8
+ accountId: string;
9
+ }
10
+
11
+ interface TriggerAccountSyncInput {
12
+ sqsClient: SQSClient;
13
+ queueUrl: string;
14
+ accountId: string;
15
+ /**
16
+ * Override the FIFO `MessageDeduplicationId`. Defaults to a time-bucketed
17
+ * manual-trigger id (`buildManualSyncDedupId`) shared by every manual call
18
+ * site (POST /sync, OAuth connect, config load, pull-to-refresh, the
19
+ * client's online-poll) so a rapid double-tap collapses into one enqueue,
20
+ * while distinct triggers a bucket or more apart always pass through.
21
+ *
22
+ * The scheduled-sync tick (#1247) must NOT share that namespace: pass
23
+ * `buildScheduledSyncDedupId()` here instead, so the two paths can never
24
+ * suppress each other.
25
+ */
26
+ dedupId?: string;
27
+ }
28
+
29
+ const isFifoQueue = (queueUrl: string): boolean => queueUrl.endsWith(".fifo");
30
+
31
+ /**
32
+ * Bucket window for the manual-trigger dedup id. SQS FIFO's own dedup window
33
+ * is 5 minutes; a static per-account id (the pre-#1250 behaviour) collided
34
+ * with the client's 5-minute online poll and swallowed a pull-to-refresh
35
+ * within 5 minutes of any prior trigger. Bucketing to ~60s keeps a rapid
36
+ * double-tap deduped while letting distinct polls a minute or more apart
37
+ * always enqueue.
38
+ */
39
+ const MANUAL_DEDUP_BUCKET_MS = 60_000;
40
+
41
+ /**
42
+ * Build the manual-trigger dedup id (POST /sync, OAuth connect, config load,
43
+ * pull-to-refresh, client online-poll), bucketed so consecutive triggers a
44
+ * bucket apart never collide — see `dedupId` above.
45
+ */
46
+ export const buildManualSyncDedupId = (
47
+ accountId: string,
48
+ now: number,
49
+ bucketMs: number = MANUAL_DEDUP_BUCKET_MS,
50
+ ): string => {
51
+ const bucket = Math.floor(now / bucketMs);
52
+ return `SYNC_MAILBOXES:manual:${accountId}:${bucket}`;
53
+ };
54
+
55
+ /**
56
+ * Build the scheduler's own dedup namespace, bucketed by tick interval so
57
+ * consecutive ticks each get a fresh id (never colliding with each other)
58
+ * while still deduping a genuine double-invocation of the same tick (e.g. a
59
+ * retried Lambda) — see `dedupId` above.
60
+ */
61
+ export const buildScheduledSyncDedupId = (
62
+ accountId: string,
63
+ now: number,
64
+ bucketMs: number,
65
+ ): string => {
66
+ const bucket = Math.floor(now / bucketMs);
67
+ return `SYNC_MAILBOXES:scheduled:${accountId}:${bucket}`;
68
+ };
69
+
70
+ export const buildSyncMailboxesCommand = (
71
+ input: TriggerAccountSyncInput,
72
+ ): SendMessageCommand => {
73
+ const { queueUrl, accountId, dedupId } = input;
74
+ const event: SyncMailboxesEvent = {
75
+ type: "SYNC_MAILBOXES",
76
+ eventId: randomUUID(),
77
+ timestamp: Date.now(),
78
+ accountId,
79
+ };
80
+
81
+ const useFifo = isFifoQueue(queueUrl);
82
+
83
+ return new SendMessageCommand({
84
+ QueueUrl: queueUrl,
85
+ MessageBody: JSON.stringify(event),
86
+ ...(useFifo && {
87
+ MessageGroupId: accountId,
88
+ MessageDeduplicationId:
89
+ dedupId ?? buildManualSyncDedupId(accountId, event.timestamp),
90
+ }),
91
+ });
92
+ };
93
+
94
+ export const triggerAccountSync = async (
95
+ input: TriggerAccountSyncInput,
96
+ ): Promise<{ eventId: string }> => {
97
+ const command = buildSyncMailboxesCommand(input);
98
+ await input.sqsClient.send(command);
99
+ const body = command.input.MessageBody ?? "{}";
100
+ const parsed = JSON.parse(body) as SyncMailboxesEvent;
101
+ return { eventId: parsed.eventId };
102
+ };
package/src/types.ts ADDED
@@ -0,0 +1,161 @@
1
+ import type { Context } from "openapi-backend";
2
+
3
+ type MatchPrefix<
4
+ Prefix extends string,
5
+ S extends string,
6
+ > = S extends `${Prefix}${infer _}` ? S : never;
7
+
8
+ export type OperationIds =
9
+ | "MeOperations_deleteMe"
10
+ | "MeOperations_listVipSuggestions"
11
+ | "MeOperations_createExport"
12
+ | "MeOperations_getExport"
13
+ | "ConfigOperations_getConfig"
14
+ | "AccountOperations_createAccount"
15
+ | "AccountOperations_testConnection"
16
+ | "AccountDetailOperations_updateAccount"
17
+ | "AccountDetailOperations_deleteAccount"
18
+ | "MicrosoftOAuthOperations_microsoftOAuthStart"
19
+ | "MicrosoftOAuthOperations_microsoftOAuthCallback"
20
+ | "MailboxOperations_listMailboxes"
21
+ | "MailboxOperations_createMailbox"
22
+ | "MailboxDetailOperations_getMailbox"
23
+ | "MailboxDetailOperations_renameMailbox"
24
+ | "MailboxDetailOperations_deleteMailbox"
25
+ | "FolderRoleOperations_appointFolderRole"
26
+ | "FilterOperations_listFilters"
27
+ | "FilterOperations_createFilter"
28
+ | "FilterDetailOperations_getFilter"
29
+ | "FilterDetailOperations_updateFilter"
30
+ | "FilterDetailOperations_deleteFilter"
31
+ | "OrganizeOperations_createOrganizeJob"
32
+ | "OrganizeOperations_previewOrganize"
33
+ | "OrganizeJobDetailOperations_getOrganizeJob"
34
+ | "SyncOperations_triggerSync"
35
+ | "SyncOperations_getSyncStatus"
36
+ | "SemanticSearchOperations_semanticSearch"
37
+ | "ThreadDetailOperations_listThreadMessages"
38
+ | "UnifiedThreadOperations_listAllThreads"
39
+ | "ThreadOperations_listThreads"
40
+ | "ThreadOperations_searchThreads"
41
+ | "MessageOperations_describeMessage"
42
+ | "MessageOperations_getRawMessage"
43
+ | "MessageOperations_updateMessageFlags"
44
+ | "MessageBulkOperations_deleteMessages"
45
+ | "MessageBulkOperations_moveMessages"
46
+ | "MessageBulkOperations_updateFlags"
47
+ | "MessageBulkOperations_copyMessages"
48
+ | "TrashOperations_emptyTrash"
49
+ | "OutboxOperations_createOutboxMessage"
50
+ | "OutboxOperations_listOutboxMessages"
51
+ | "OutboxDetailOperations_getOutboxMessage"
52
+ | "OutboxDetailOperations_updateOutboxMessage"
53
+ | "OutboxDetailOperations_deleteOutboxMessage"
54
+ | "OutboxDetailOperations_sendOutboxMessage"
55
+ | "AddressOperations_searchAddresses"
56
+ | "AddressDetailOperations_updateAddress";
57
+
58
+ export type MeOperationIds = MatchPrefix<"MeOperations_", OperationIds>;
59
+
60
+ export type ConfigOperationIds = MatchPrefix<"ConfigOperations_", OperationIds>;
61
+
62
+ export type AccountOperationIds = MatchPrefix<
63
+ "AccountOperations_",
64
+ OperationIds
65
+ >;
66
+
67
+ export type AccountDetailOperationIds = MatchPrefix<
68
+ "AccountDetailOperations_",
69
+ OperationIds
70
+ >;
71
+
72
+ export type MailboxOperationIds = MatchPrefix<
73
+ "MailboxOperations_",
74
+ OperationIds
75
+ >;
76
+
77
+ export type MailboxDetailOperationIds = MatchPrefix<
78
+ "MailboxDetailOperations_",
79
+ OperationIds
80
+ >;
81
+
82
+ export type FolderRoleOperationIds = MatchPrefix<
83
+ "FolderRoleOperations_",
84
+ OperationIds
85
+ >;
86
+
87
+ export type FilterOperationIds = MatchPrefix<"FilterOperations_", OperationIds>;
88
+
89
+ export type FilterDetailOperationIds = MatchPrefix<
90
+ "FilterDetailOperations_",
91
+ OperationIds
92
+ >;
93
+
94
+ export type OrganizeOperationIds = MatchPrefix<
95
+ "OrganizeOperations_",
96
+ OperationIds
97
+ >;
98
+
99
+ export type OrganizeJobDetailOperationIds = MatchPrefix<
100
+ "OrganizeJobDetailOperations_",
101
+ OperationIds
102
+ >;
103
+
104
+ export type SyncOperationIds = MatchPrefix<"SyncOperations_", OperationIds>;
105
+
106
+ export type ThreadDetailOperationIds = MatchPrefix<
107
+ "ThreadDetailOperations_",
108
+ OperationIds
109
+ >;
110
+
111
+ export type UnifiedThreadOperationIds = MatchPrefix<
112
+ "UnifiedThreadOperations_",
113
+ OperationIds
114
+ >;
115
+
116
+ export type ThreadOperationIds = MatchPrefix<"ThreadOperations_", OperationIds>;
117
+
118
+ export type SemanticSearchOperationIds = MatchPrefix<
119
+ "SemanticSearchOperations_",
120
+ OperationIds
121
+ >;
122
+
123
+ export type MessageOperationIds = MatchPrefix<
124
+ "MessageOperations_",
125
+ OperationIds
126
+ >;
127
+
128
+ export type MessageBulkOperationIds = MatchPrefix<
129
+ "MessageBulkOperations_",
130
+ OperationIds
131
+ >;
132
+
133
+ export type TrashOperationIds = MatchPrefix<"TrashOperations_", OperationIds>;
134
+
135
+ export type OutboxOperationIds = MatchPrefix<"OutboxOperations_", OperationIds>;
136
+
137
+ export type OutboxDetailOperationIds = MatchPrefix<
138
+ "OutboxDetailOperations_",
139
+ OperationIds
140
+ >;
141
+
142
+ export type AddressOperationIds = MatchPrefix<
143
+ "AddressOperations_",
144
+ OperationIds
145
+ >;
146
+
147
+ export type AddressDetailOperationIds = MatchPrefix<
148
+ "AddressDetailOperations_",
149
+ OperationIds
150
+ >;
151
+
152
+ export type MicrosoftOAuthOperationIds = MatchPrefix<
153
+ "MicrosoftOAuthOperations_",
154
+ OperationIds
155
+ >;
156
+
157
+ // biome-ignore lint/suspicious/noExplicitAny: handler responses vary by operation
158
+ type HandlerResponse = Record<string, any>;
159
+ export type OperationHandler<_T extends OperationIds = OperationIds> = (
160
+ context: Context,
161
+ ) => Promise<HandlerResponse>;
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist"
5
+ },
6
+ "include": ["src/**/*.ts", "dev-server/**/*.ts"]
7
+ }