@remit/imap-worker 0.0.90 → 0.0.91
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.
- package/dist/account-check.d.ts +27 -0
- package/dist/account-check.js +70 -0
- package/dist/body-sync-gate.d.ts +12 -0
- package/dist/body-sync-gate.js +53 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +193 -0
- package/dist/config-env.d.ts +5 -0
- package/dist/config-env.js +37 -0
- package/dist/connection-pool.d.ts +22 -0
- package/dist/connection-pool.js +97 -0
- package/dist/connection-scope.d.ts +8 -0
- package/dist/connection-scope.js +10 -0
- package/dist/context.d.ts +46 -0
- package/dist/context.js +15 -0
- package/dist/e2e-processor-shim.d.ts +2 -0
- package/dist/e2e-processor-shim.js +181 -0
- package/dist/emit.d.ts +15 -0
- package/dist/emit.js +76 -0
- package/dist/events.d.ts +203 -0
- package/dist/events.js +1 -0
- package/dist/failure-kind.d.ts +10 -0
- package/{src/failure-kind.ts → dist/failure-kind.js} +6 -6
- package/dist/handlers/append-sent-message.d.ts +3 -0
- package/dist/handlers/append-sent-message.js +152 -0
- package/dist/handlers/delete-account-objects.d.ts +3 -0
- package/dist/handlers/delete-account-objects.js +35 -0
- package/dist/handlers/empty-trash.d.ts +14 -0
- package/dist/handlers/empty-trash.js +217 -0
- package/dist/handlers/flag-push.d.ts +38 -0
- package/dist/handlers/flag-push.js +195 -0
- package/dist/handlers/imported-folder.d.ts +4 -0
- package/dist/handlers/imported-folder.js +23 -0
- package/dist/handlers/mailbox-management.d.ts +6 -0
- package/dist/handlers/mailbox-management.js +294 -0
- package/dist/handlers/mailbox-sync-order.d.ts +21 -0
- package/dist/handlers/mailbox-sync-order.js +50 -0
- package/dist/handlers/message-copy.d.ts +40 -0
- package/dist/handlers/message-copy.js +371 -0
- package/dist/handlers/message-delete-terminal.d.ts +60 -0
- package/dist/handlers/message-delete-terminal.js +77 -0
- package/dist/handlers/message-delete.d.ts +57 -0
- package/dist/handlers/message-delete.js +520 -0
- package/{src/handlers/message-move-terminal.ts → dist/handlers/message-move-terminal.d.ts} +18 -105
- package/dist/handlers/message-move-terminal.js +94 -0
- package/dist/handlers/message-move.d.ts +146 -0
- package/dist/handlers/message-move.js +490 -0
- package/dist/handlers/placement-move-push.d.ts +62 -0
- package/dist/handlers/placement-move-push.js +307 -0
- package/dist/handlers/probe-message-id.d.ts +2 -0
- package/dist/handlers/probe-message-id.js +6 -0
- package/dist/handlers/sync-mailboxes.d.ts +45 -0
- package/dist/handlers/sync-mailboxes.js +233 -0
- package/dist/handlers/sync-message-body.d.ts +23 -0
- package/dist/handlers/sync-message-body.js +182 -0
- package/dist/handlers/sync-messages.d.ts +29 -0
- package/dist/handlers/sync-messages.js +285 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +67 -0
- package/dist/lambda-context.d.ts +8 -0
- package/dist/lambda-context.js +33 -0
- package/dist/mailbox-row.d.ts +9 -0
- package/dist/mailbox-row.js +6 -0
- package/dist/poller.d.ts +1 -0
- package/dist/poller.js +51 -0
- package/dist/process.d.ts +6 -0
- package/dist/process.js +3 -0
- package/dist/processor.d.ts +3 -0
- package/dist/processor.js +44 -0
- package/dist/queues.d.ts +10 -0
- package/dist/queues.js +1 -0
- package/dist/s3-account-objects.d.ts +3 -0
- package/dist/s3-account-objects.js +26 -0
- package/dist/scheduler/attachment-sweep.d.ts +11 -0
- package/dist/scheduler/attachment-sweep.js +23 -0
- package/dist/scheduler/calendar-subscriptions.d.ts +26 -0
- package/dist/scheduler/calendar-subscriptions.js +35 -0
- package/dist/scheduler/config.d.ts +27 -0
- package/{src/scheduler/config.ts → dist/scheduler/config.js} +7 -26
- package/{src/scheduler/decide-due.ts → dist/scheduler/decide-due.d.ts} +3 -12
- package/dist/scheduler/decide-due.js +17 -0
- package/dist/scheduler/handler.d.ts +21 -0
- package/{src/scheduler/handler.ts → dist/scheduler/handler.js} +25 -42
- package/{src/scheduler/loop.ts → dist/scheduler/loop.d.ts} +7 -23
- package/dist/scheduler/loop.js +8 -0
- package/dist/scheduler/run-tick.d.ts +62 -0
- package/dist/scheduler/run-tick.js +164 -0
- package/dist/scheduler/runner.d.ts +2 -0
- package/{src/scheduler/runner.ts → dist/scheduler/runner.js} +57 -78
- package/dist/scheduler/stranded-placement-redrive.d.ts +16 -0
- package/dist/scheduler/stranded-placement-redrive.js +172 -0
- package/dist/sqs-queues.d.ts +4 -0
- package/dist/sqs-queues.js +22 -0
- package/dist/with-oauth-lifecycle-deps.d.ts +5 -0
- package/dist/with-oauth-lifecycle-deps.js +17 -0
- package/dist/with-oauth-lifecycle.d.ts +56 -0
- package/dist/with-oauth-lifecycle.js +92 -0
- package/{src/worker-version.ts → dist/worker-version.d.ts} +1 -2
- package/dist/worker-version.js +12 -0
- package/package.json +75 -69
- package/src/account-check.test.ts +0 -149
- package/src/account-check.ts +0 -101
- package/src/body-sync-gate.test.ts +0 -185
- package/src/body-sync-gate.ts +0 -86
- package/src/cli.ts +0 -218
- package/src/connection-scope.test.ts +0 -266
- package/src/connection-scope.ts +0 -314
- package/src/credential-less-sync-fence.test.ts +0 -152
- package/src/e2e-processor-shim.ts +0 -232
- package/src/emit.test.ts +0 -145
- package/src/emit.ts +0 -115
- package/src/events.ts +0 -252
- package/src/failure-kind.test.ts +0 -42
- package/src/handlers/append-sent-message.sqlite.test.ts +0 -176
- package/src/handlers/append-sent-message.ts +0 -264
- package/src/handlers/delete-account-objects.test.ts +0 -126
- package/src/handlers/delete-account-objects.ts +0 -96
- package/src/handlers/empty-trash.sqlite.test.ts +0 -209
- package/src/handlers/empty-trash.ts +0 -352
- package/src/handlers/flag-push.ts +0 -285
- package/src/handlers/imported-folder.test.ts +0 -63
- package/src/handlers/imported-folder.ts +0 -38
- package/src/handlers/mailbox-management.sqlite.test.ts +0 -265
- package/src/handlers/mailbox-management.ts +0 -486
- package/src/handlers/mailbox-sync-order.test.ts +0 -93
- package/src/handlers/mailbox-sync-order.ts +0 -65
- package/src/handlers/message-copy.sqlite.test.ts +0 -298
- package/src/handlers/message-copy.ts +0 -569
- package/src/handlers/message-delete-terminal.ts +0 -147
- package/src/handlers/message-delete.sqlite.test.ts +0 -291
- package/src/handlers/message-delete.ts +0 -888
- package/src/handlers/message-move.sqlite.test.ts +0 -271
- package/src/handlers/message-move.test.ts +0 -216
- package/src/handlers/message-move.ts +0 -755
- package/src/handlers/placement-move-push.test.ts +0 -137
- package/src/handlers/placement-move-push.ts +0 -478
- package/src/handlers/placement-push.sqlite.test.ts +0 -299
- package/src/handlers/probe-message-id.sqlite.test.ts +0 -192
- package/src/handlers/probe-message-id.ts +0 -15
- package/src/handlers/sync-mailboxes.test.ts +0 -183
- package/src/handlers/sync-mailboxes.ts +0 -425
- package/src/handlers/sync-message-body.sqlite.test.ts +0 -314
- package/src/handlers/sync-message-body.test.ts +0 -150
- package/src/handlers/sync-message-body.ts +0 -366
- package/src/handlers/sync-messages.sqlite.test.ts +0 -195
- package/src/handlers/sync-messages.ts +0 -537
- package/src/index.test.ts +0 -22
- package/src/index.ts +0 -82
- package/src/mail-sync-selection.sqlite.test.ts +0 -227
- package/src/mailbox-row.ts +0 -20
- package/src/pending-sync.sqlite.test.ts +0 -165
- package/src/poller.ts +0 -55
- package/src/processor.ts +0 -62
- package/src/queues.ts +0 -40
- package/src/scheduler/attachment-sweep.sqlite.test.ts +0 -74
- package/src/scheduler/attachment-sweep.ts +0 -46
- package/src/scheduler/calendar-subscriptions.sqlite.test.ts +0 -147
- package/src/scheduler/calendar-subscriptions.ts +0 -103
- package/src/scheduler/config.test.ts +0 -40
- package/src/scheduler/decide-due.test.ts +0 -44
- package/src/scheduler/half-deleted-accounts.sqlite.test.ts +0 -196
- package/src/scheduler/loop.test.ts +0 -143
- package/src/scheduler/run-tick.sqlite.test.ts +0 -191
- package/src/scheduler/run-tick.ts +0 -281
- package/src/scheduler/stranded-placement-redrive.ts +0 -265
- package/src/stranded-placement.sqlite.test.ts +0 -799
- package/src/test-helpers/test-world.ts +0 -425
- package/src/test-helpers/worker-queues.ts +0 -11
- package/src/thread-join.sqlite.test.ts +0 -106
- package/src/with-oauth-lifecycle-deps.sqlite.test.ts +0 -269
- package/src/with-oauth-lifecycle-deps.ts +0 -71
- package/src/with-oauth-lifecycle.ts +0 -158
- package/tsconfig.json +0 -8
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
3
|
-
import {
|
|
4
|
-
GetParameterCommand,
|
|
5
|
-
ParameterNotFound,
|
|
6
|
-
SSMClient,
|
|
7
|
-
} from "@aws-sdk/client-ssm";
|
|
8
|
-
import type { Logger } from "@remit/logger";
|
|
9
|
-
import { mockClient } from "aws-sdk-client-mock";
|
|
10
|
-
import { isBodySyncEnabled, resetBodySyncGateCache } from "./body-sync-gate.js";
|
|
11
|
-
|
|
12
|
-
const parameterName = "/dev/Remit/bodySyncEnabled";
|
|
13
|
-
|
|
14
|
-
interface CapturedWarn {
|
|
15
|
-
args: unknown[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const createCapturingLogger = (): {
|
|
19
|
-
log: Logger;
|
|
20
|
-
warnCalls: CapturedWarn[];
|
|
21
|
-
} => {
|
|
22
|
-
const warnCalls: CapturedWarn[] = [];
|
|
23
|
-
const noop = () => {};
|
|
24
|
-
const log = {
|
|
25
|
-
info: noop,
|
|
26
|
-
warn: (...args: unknown[]) => warnCalls.push({ args }),
|
|
27
|
-
error: noop,
|
|
28
|
-
debug: noop,
|
|
29
|
-
fatal: noop,
|
|
30
|
-
trace: noop,
|
|
31
|
-
child: () => log,
|
|
32
|
-
} as unknown as Logger;
|
|
33
|
-
return { log, warnCalls };
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const warnMessage = (warn: CapturedWarn): string => String(warn.args[1]);
|
|
37
|
-
|
|
38
|
-
describe("isBodySyncEnabled", () => {
|
|
39
|
-
beforeEach(() => {
|
|
40
|
-
resetBodySyncGateCache();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
afterEach(() => {
|
|
44
|
-
mockClient(SSMClient).reset();
|
|
45
|
-
resetBodySyncGateCache();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("returns true when the parameter is 'true'", async () => {
|
|
49
|
-
const ssmMock = mockClient(SSMClient);
|
|
50
|
-
ssmMock
|
|
51
|
-
.on(GetParameterCommand, { Name: parameterName })
|
|
52
|
-
.resolves({ Parameter: { Value: "true" } });
|
|
53
|
-
const { log } = createCapturingLogger();
|
|
54
|
-
|
|
55
|
-
const enabled = await isBodySyncEnabled(
|
|
56
|
-
parameterName,
|
|
57
|
-
log,
|
|
58
|
-
ssmMock as unknown as SSMClient,
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
assert.equal(enabled, true);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("returns false when the parameter is 'false'", async () => {
|
|
65
|
-
const ssmMock = mockClient(SSMClient);
|
|
66
|
-
ssmMock
|
|
67
|
-
.on(GetParameterCommand, { Name: parameterName })
|
|
68
|
-
.resolves({ Parameter: { Value: "false" } });
|
|
69
|
-
const { log } = createCapturingLogger();
|
|
70
|
-
|
|
71
|
-
const enabled = await isBodySyncEnabled(
|
|
72
|
-
parameterName,
|
|
73
|
-
log,
|
|
74
|
-
ssmMock as unknown as SSMClient,
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
assert.equal(enabled, false);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("treats casing and whitespace as disabled for ' FALSE '", async () => {
|
|
81
|
-
const ssmMock = mockClient(SSMClient);
|
|
82
|
-
ssmMock
|
|
83
|
-
.on(GetParameterCommand, { Name: parameterName })
|
|
84
|
-
.resolves({ Parameter: { Value: " FALSE " } });
|
|
85
|
-
const { log } = createCapturingLogger();
|
|
86
|
-
|
|
87
|
-
const enabled = await isBodySyncEnabled(
|
|
88
|
-
parameterName,
|
|
89
|
-
log,
|
|
90
|
-
ssmMock as unknown as SSMClient,
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
assert.equal(enabled, false);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("fails open with a warning when the parameter does not exist", async () => {
|
|
97
|
-
const ssmMock = mockClient(SSMClient);
|
|
98
|
-
ssmMock
|
|
99
|
-
.on(GetParameterCommand, { Name: parameterName })
|
|
100
|
-
.rejects(new ParameterNotFound({ message: "not found", $metadata: {} }));
|
|
101
|
-
const { log, warnCalls } = createCapturingLogger();
|
|
102
|
-
|
|
103
|
-
const enabled = await isBodySyncEnabled(
|
|
104
|
-
parameterName,
|
|
105
|
-
log,
|
|
106
|
-
ssmMock as unknown as SSMClient,
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
assert.equal(enabled, true);
|
|
110
|
-
assert.equal(warnCalls.length, 1);
|
|
111
|
-
assert.match(warnMessage(warnCalls[0]), /failing open/i);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("fails open with a warning when the parameter has no value", async () => {
|
|
115
|
-
const ssmMock = mockClient(SSMClient);
|
|
116
|
-
ssmMock
|
|
117
|
-
.on(GetParameterCommand, { Name: parameterName })
|
|
118
|
-
.resolves({ Parameter: {} });
|
|
119
|
-
const { log, warnCalls } = createCapturingLogger();
|
|
120
|
-
|
|
121
|
-
const enabled = await isBodySyncEnabled(
|
|
122
|
-
parameterName,
|
|
123
|
-
log,
|
|
124
|
-
ssmMock as unknown as SSMClient,
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
assert.equal(enabled, true);
|
|
128
|
-
assert.equal(warnCalls.length, 1);
|
|
129
|
-
assert.match(warnMessage(warnCalls[0]), /missing a value/i);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("caches the result within the TTL — second call does not re-fetch", async () => {
|
|
133
|
-
const ssmMock = mockClient(SSMClient);
|
|
134
|
-
ssmMock
|
|
135
|
-
.on(GetParameterCommand, { Name: parameterName })
|
|
136
|
-
.resolves({ Parameter: { Value: "false" } });
|
|
137
|
-
const { log } = createCapturingLogger();
|
|
138
|
-
|
|
139
|
-
const first = await isBodySyncEnabled(
|
|
140
|
-
parameterName,
|
|
141
|
-
log,
|
|
142
|
-
ssmMock as unknown as SSMClient,
|
|
143
|
-
);
|
|
144
|
-
const second = await isBodySyncEnabled(
|
|
145
|
-
parameterName,
|
|
146
|
-
log,
|
|
147
|
-
ssmMock as unknown as SSMClient,
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
assert.equal(first, false);
|
|
151
|
-
assert.equal(second, false);
|
|
152
|
-
assert.equal(ssmMock.commandCalls(GetParameterCommand).length, 1);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it("coalesces concurrent cold-cache calls into a single GetParameter", async () => {
|
|
156
|
-
const ssmMock = mockClient(SSMClient);
|
|
157
|
-
let resolveSend:
|
|
158
|
-
| ((value: { Parameter: { Value: string } }) => void)
|
|
159
|
-
| undefined;
|
|
160
|
-
ssmMock.on(GetParameterCommand, { Name: parameterName }).callsFake(
|
|
161
|
-
() =>
|
|
162
|
-
new Promise((resolve) => {
|
|
163
|
-
resolveSend = resolve;
|
|
164
|
-
}),
|
|
165
|
-
);
|
|
166
|
-
const { log } = createCapturingLogger();
|
|
167
|
-
|
|
168
|
-
const a = isBodySyncEnabled(
|
|
169
|
-
parameterName,
|
|
170
|
-
log,
|
|
171
|
-
ssmMock as unknown as SSMClient,
|
|
172
|
-
);
|
|
173
|
-
const b = isBodySyncEnabled(
|
|
174
|
-
parameterName,
|
|
175
|
-
log,
|
|
176
|
-
ssmMock as unknown as SSMClient,
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
assert.ok(resolveSend, "GetParameter should have been invoked");
|
|
180
|
-
resolveSend?.({ Parameter: { Value: "true" } });
|
|
181
|
-
|
|
182
|
-
assert.deepEqual(await Promise.all([a, b]), [true, true]);
|
|
183
|
-
assert.equal(ssmMock.commandCalls(GetParameterCommand).length, 1);
|
|
184
|
-
});
|
|
185
|
-
});
|
package/src/body-sync-gate.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
|
|
2
|
-
import type { Logger } from "@remit/logger";
|
|
3
|
-
|
|
4
|
-
const CACHE_TTL_MS = 30_000;
|
|
5
|
-
|
|
6
|
-
interface CacheEntry {
|
|
7
|
-
readonly enabled: boolean;
|
|
8
|
-
readonly expiresAt: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
let cache: CacheEntry | undefined;
|
|
12
|
-
let inFlight: Promise<boolean> | undefined;
|
|
13
|
-
let defaultClient: SSMClient | undefined;
|
|
14
|
-
|
|
15
|
-
const getDefaultClient = (): SSMClient => {
|
|
16
|
-
if (!defaultClient) {
|
|
17
|
-
defaultClient = new SSMClient({});
|
|
18
|
-
}
|
|
19
|
-
return defaultClient;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Test seam — clears the module-scope TTL cache between unit tests.
|
|
24
|
-
*/
|
|
25
|
-
export const resetBodySyncGateCache = (): void => {
|
|
26
|
-
cache = undefined;
|
|
27
|
-
inFlight = undefined;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const readParameter = async (
|
|
31
|
-
ssm: SSMClient,
|
|
32
|
-
parameterName: string,
|
|
33
|
-
log: Logger,
|
|
34
|
-
): Promise<boolean> => {
|
|
35
|
-
const result = await ssm.send(
|
|
36
|
-
new GetParameterCommand({ Name: parameterName }),
|
|
37
|
-
);
|
|
38
|
-
const value = result.Parameter?.Value;
|
|
39
|
-
|
|
40
|
-
if (value === undefined) {
|
|
41
|
-
log.warn(
|
|
42
|
-
{ parameterName },
|
|
43
|
-
"Body-sync toggle parameter missing a value, failing open (enabled)",
|
|
44
|
-
);
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return value.trim().toLowerCase() !== "false";
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Reads the `/{stage}/Remit/bodySyncEnabled` SSM parameter, cached at module
|
|
53
|
-
* scope for a short TTL. Returns `true` (enabled) when the parameter is absent
|
|
54
|
-
* or unreadable — body sync fails open so a missing toggle never halts prod.
|
|
55
|
-
*/
|
|
56
|
-
export const isBodySyncEnabled = async (
|
|
57
|
-
parameterName: string,
|
|
58
|
-
log: Logger,
|
|
59
|
-
ssm: SSMClient = getDefaultClient(),
|
|
60
|
-
): Promise<boolean> => {
|
|
61
|
-
if (cache && cache.expiresAt > Date.now()) {
|
|
62
|
-
return cache.enabled;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (inFlight) {
|
|
66
|
-
return inFlight;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
inFlight = readParameter(ssm, parameterName, log)
|
|
70
|
-
.catch((error) => {
|
|
71
|
-
log.warn(
|
|
72
|
-
{ parameterName, error },
|
|
73
|
-
"Failed to read body-sync toggle parameter, failing open (enabled)",
|
|
74
|
-
);
|
|
75
|
-
return true;
|
|
76
|
-
})
|
|
77
|
-
.then((enabled) => {
|
|
78
|
-
cache = { enabled, expiresAt: Date.now() + CACHE_TTL_MS };
|
|
79
|
-
return enabled;
|
|
80
|
-
})
|
|
81
|
-
.finally(() => {
|
|
82
|
-
inFlight = undefined;
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
return inFlight;
|
|
86
|
-
};
|
package/src/cli.ts
DELETED
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Plain text on purpose, and the one deliberate exception to the log field
|
|
3
|
-
// contract in deploy/vps/README.md ("Logs"). That contract covers what a
|
|
4
|
-
// container's log driver ships; this file is the `remit-worker` bin, run by hand
|
|
5
|
-
// against a terminal, and is not an entrypoint of any image (see the target list
|
|
6
|
-
// in npm-scripts/docker-bundle.mjs). Do not route it through the logger — usage
|
|
7
|
-
// text and argument errors are for the person who typed the command.
|
|
8
|
-
import { parseArgs } from "node:util";
|
|
9
|
-
import { createEmitEvent } from "./emit.js";
|
|
10
|
-
import { sqsWorkerQueues, workerQueueUrlsFromEnv } from "./queues.js";
|
|
11
|
-
|
|
12
|
-
const HELP = `
|
|
13
|
-
remit-imap-worker - Process IMAP sync events
|
|
14
|
-
|
|
15
|
-
USAGE:
|
|
16
|
-
remit-worker -t <type> -a <accountId> [options]
|
|
17
|
-
|
|
18
|
-
OPTIONS:
|
|
19
|
-
-t, --type <type> Event type (required)
|
|
20
|
-
-a, --accountId <id> Account ID (required)
|
|
21
|
-
-m, --mailboxId <id> Mailbox ID (required for some event types)
|
|
22
|
-
--messageIds <ids> Comma-separated message IDs (required for SYNC_MESSAGE_BODY)
|
|
23
|
-
--fullSync Force full sync, ignoring lastSyncUid (SYNC_MESSAGES only)
|
|
24
|
-
--path <path> Mailbox path (required for MAILBOX_CREATE, MAILBOX_DELETE)
|
|
25
|
-
--oldPath <path> Old mailbox path (required for MAILBOX_RENAME)
|
|
26
|
-
--newPath <path> New mailbox path (required for MAILBOX_RENAME)
|
|
27
|
-
--subscribe Subscribe to mailbox after creation (MAILBOX_CREATE only)
|
|
28
|
-
-h, --help Show this help message
|
|
29
|
-
|
|
30
|
-
EVENT TYPES:
|
|
31
|
-
SYNC_MAILBOXES Sync all mailboxes for an account
|
|
32
|
-
SYNC_MESSAGES Sync messages in a specific mailbox
|
|
33
|
-
SYNC_MESSAGE_BODY Fetch and store message bodies in batch
|
|
34
|
-
MAILBOX_CREATE Create a new mailbox
|
|
35
|
-
MAILBOX_RENAME Rename a mailbox
|
|
36
|
-
MAILBOX_DELETE Delete a mailbox
|
|
37
|
-
|
|
38
|
-
EXAMPLES:
|
|
39
|
-
# Sync all mailboxes for an account
|
|
40
|
-
remit-worker -t SYNC_MAILBOXES -a account-123
|
|
41
|
-
|
|
42
|
-
# Sync messages in a mailbox
|
|
43
|
-
remit-worker -t SYNC_MESSAGES -a account-123 -m mailbox-456
|
|
44
|
-
|
|
45
|
-
# Force a full sync of messages (ignore lastSyncUid)
|
|
46
|
-
remit-worker -t SYNC_MESSAGES -a account-123 -m mailbox-456 --fullSync
|
|
47
|
-
|
|
48
|
-
# Sync message bodies for specific messages
|
|
49
|
-
remit-worker -t SYNC_MESSAGE_BODY -a account-123 -m mailbox-456 --messageIds id1,id2,id3
|
|
50
|
-
|
|
51
|
-
# Create a new mailbox
|
|
52
|
-
remit-worker -t MAILBOX_CREATE -a account-123 -m mailbox-id --path Work/Projects --subscribe
|
|
53
|
-
|
|
54
|
-
# Rename a mailbox
|
|
55
|
-
remit-worker -t MAILBOX_RENAME -a account-123 -m mailbox-id --oldPath Work/Projects --newPath Archive/Projects
|
|
56
|
-
|
|
57
|
-
# Delete a mailbox
|
|
58
|
-
remit-worker -t MAILBOX_DELETE -a account-123 -m mailbox-id --path Work/Projects
|
|
59
|
-
`;
|
|
60
|
-
|
|
61
|
-
const { values } = parseArgs({
|
|
62
|
-
options: {
|
|
63
|
-
type: { type: "string", short: "t" },
|
|
64
|
-
accountId: { type: "string", short: "a" },
|
|
65
|
-
mailboxId: { type: "string", short: "m" },
|
|
66
|
-
messageIds: { type: "string" },
|
|
67
|
-
fullSync: { type: "boolean", default: false },
|
|
68
|
-
path: { type: "string" },
|
|
69
|
-
oldPath: { type: "string" },
|
|
70
|
-
newPath: { type: "string" },
|
|
71
|
-
subscribe: { type: "boolean", default: false },
|
|
72
|
-
help: { type: "boolean", short: "h", default: false },
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
if (values.help) {
|
|
77
|
-
console.log(HELP);
|
|
78
|
-
process.exit(0);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (!values.type || !values.accountId) {
|
|
82
|
-
console.error("Error: --type and --accountId are required\n");
|
|
83
|
-
console.log(HELP);
|
|
84
|
-
process.exit(0);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const validTypes = [
|
|
88
|
-
"SYNC_MAILBOXES",
|
|
89
|
-
"SYNC_MESSAGES",
|
|
90
|
-
"SYNC_MESSAGE_BODY",
|
|
91
|
-
"MAILBOX_CREATE",
|
|
92
|
-
"MAILBOX_RENAME",
|
|
93
|
-
"MAILBOX_DELETE",
|
|
94
|
-
];
|
|
95
|
-
if (!validTypes.includes(values.type)) {
|
|
96
|
-
console.error(
|
|
97
|
-
`Error: Invalid type "${values.type}". Must be one of: ${validTypes.join(", ")}\n`,
|
|
98
|
-
);
|
|
99
|
-
process.exit(1);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (
|
|
103
|
-
["SYNC_MESSAGES", "SYNC_MESSAGE_BODY"].includes(values.type) &&
|
|
104
|
-
!values.mailboxId
|
|
105
|
-
) {
|
|
106
|
-
console.error(`Error: --mailboxId is required for ${values.type}\n`);
|
|
107
|
-
process.exit(1);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (values.type === "SYNC_MESSAGE_BODY" && !values.messageIds) {
|
|
111
|
-
console.error(`Error: --messageIds is required for ${values.type}\n`);
|
|
112
|
-
process.exit(1);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Validation for mailbox management events
|
|
116
|
-
if (
|
|
117
|
-
["MAILBOX_CREATE", "MAILBOX_DELETE"].includes(values.type) &&
|
|
118
|
-
!values.path
|
|
119
|
-
) {
|
|
120
|
-
console.error(`Error: --path is required for ${values.type}\n`);
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
["MAILBOX_CREATE", "MAILBOX_RENAME", "MAILBOX_DELETE"].includes(
|
|
126
|
-
values.type,
|
|
127
|
-
) &&
|
|
128
|
-
!values.mailboxId
|
|
129
|
-
) {
|
|
130
|
-
console.error(`Error: --mailboxId is required for ${values.type}\n`);
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (values.type === "MAILBOX_RENAME" && (!values.oldPath || !values.newPath)) {
|
|
135
|
-
console.error(
|
|
136
|
-
`Error: --oldPath and --newPath are required for ${values.type}\n`,
|
|
137
|
-
);
|
|
138
|
-
process.exit(1);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Build the event based on type
|
|
142
|
-
const buildEvent = () => {
|
|
143
|
-
switch (values.type) {
|
|
144
|
-
case "SYNC_MAILBOXES":
|
|
145
|
-
return {
|
|
146
|
-
type: "SYNC_MAILBOXES" as const,
|
|
147
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
148
|
-
accountId: values.accountId!,
|
|
149
|
-
};
|
|
150
|
-
case "SYNC_MESSAGES":
|
|
151
|
-
return {
|
|
152
|
-
type: "SYNC_MESSAGES" as const,
|
|
153
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
154
|
-
accountId: values.accountId!,
|
|
155
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
156
|
-
mailboxId: values.mailboxId!,
|
|
157
|
-
fullSync: values.fullSync,
|
|
158
|
-
};
|
|
159
|
-
case "SYNC_MESSAGE_BODY":
|
|
160
|
-
return {
|
|
161
|
-
type: "SYNC_MESSAGE_BODY" as const,
|
|
162
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
163
|
-
accountId: values.accountId!,
|
|
164
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
165
|
-
mailboxId: values.mailboxId!,
|
|
166
|
-
messageIds: values.messageIds?.split(",").map((id) => id.trim()),
|
|
167
|
-
};
|
|
168
|
-
case "MAILBOX_CREATE":
|
|
169
|
-
return {
|
|
170
|
-
type: "MAILBOX_CREATE" as const,
|
|
171
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
172
|
-
accountId: values.accountId!,
|
|
173
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
174
|
-
mailboxId: values.mailboxId!,
|
|
175
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
176
|
-
path: values.path!,
|
|
177
|
-
subscribe: values.subscribe,
|
|
178
|
-
};
|
|
179
|
-
case "MAILBOX_RENAME":
|
|
180
|
-
return {
|
|
181
|
-
type: "MAILBOX_RENAME" as const,
|
|
182
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
183
|
-
accountId: values.accountId!,
|
|
184
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
185
|
-
mailboxId: values.mailboxId!,
|
|
186
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
187
|
-
oldPath: values.oldPath!,
|
|
188
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
189
|
-
newPath: values.newPath!,
|
|
190
|
-
};
|
|
191
|
-
case "MAILBOX_DELETE":
|
|
192
|
-
return {
|
|
193
|
-
type: "MAILBOX_DELETE" as const,
|
|
194
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
195
|
-
accountId: values.accountId!,
|
|
196
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
197
|
-
mailboxId: values.mailboxId!,
|
|
198
|
-
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed by caller contract
|
|
199
|
-
path: values.path!,
|
|
200
|
-
};
|
|
201
|
-
default:
|
|
202
|
-
throw new Error(`Unknown event type: ${values.type}`);
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
const event = buildEvent();
|
|
207
|
-
|
|
208
|
-
console.log(`Enqueueing ${event.type} event for account ${event.accountId}...`);
|
|
209
|
-
|
|
210
|
-
createEmitEvent(sqsWorkerQueues(workerQueueUrlsFromEnv()))(event)
|
|
211
|
-
.then(() => {
|
|
212
|
-
console.log("Event enqueued successfully");
|
|
213
|
-
process.exit(0);
|
|
214
|
-
})
|
|
215
|
-
.catch((error) => {
|
|
216
|
-
console.error("Failed to enqueue event:", error);
|
|
217
|
-
process.exit(1);
|
|
218
|
-
});
|