@diskd-ai/email-client-mcp 0.1.34 → 0.1.39
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/config/schema.d.ts +535 -5
- package/dist/config/schema.js +71 -6
- package/dist/config/schema.js.map +1 -1
- package/dist/delivery/application/decideTransportOutcome.d.ts +15 -0
- package/dist/delivery/application/decideTransportOutcome.js +14 -0
- package/dist/delivery/application/decideTransportOutcome.js.map +1 -0
- package/dist/delivery/application/outboxRepository.d.ts +83 -0
- package/dist/delivery/application/outboxRepository.js +2 -0
- package/dist/delivery/application/outboxRepository.js.map +1 -0
- package/dist/delivery/domain/deliveryProgress.d.ts +17 -0
- package/dist/delivery/domain/deliveryProgress.js +34 -0
- package/dist/delivery/domain/deliveryProgress.js.map +1 -0
- package/dist/delivery/domain/deliveryState.d.ts +21 -0
- package/dist/delivery/domain/deliveryState.js +7 -0
- package/dist/delivery/domain/deliveryState.js.map +1 -0
- package/dist/delivery/domain/identity.d.ts +28 -0
- package/dist/delivery/domain/identity.js +28 -0
- package/dist/delivery/domain/identity.js.map +1 -0
- package/dist/delivery/domain/json.d.ts +5 -0
- package/dist/delivery/domain/json.js +2 -0
- package/dist/delivery/domain/json.js.map +1 -0
- package/dist/delivery/domain/transportOutcome.d.ts +31 -0
- package/dist/delivery/domain/transportOutcome.js +2 -0
- package/dist/delivery/domain/transportOutcome.js.map +1 -0
- package/dist/delivery/infrastructure/deliveryProcessor.d.ts +42 -0
- package/dist/delivery/infrastructure/deliveryProcessor.js +179 -0
- package/dist/delivery/infrastructure/deliveryProcessor.js.map +1 -0
- package/dist/delivery/infrastructure/deliveryRuntime.d.ts +17 -0
- package/dist/delivery/infrastructure/deliveryRuntime.js +186 -0
- package/dist/delivery/infrastructure/deliveryRuntime.js.map +1 -0
- package/dist/delivery/infrastructure/emailOutboxPayload.d.ts +7 -0
- package/dist/delivery/infrastructure/emailOutboxPayload.js +42 -0
- package/dist/delivery/infrastructure/emailOutboxPayload.js.map +1 -0
- package/dist/delivery/infrastructure/outboxEvent.d.ts +30 -0
- package/dist/delivery/infrastructure/outboxEvent.js +72 -0
- package/dist/delivery/infrastructure/outboxEvent.js.map +1 -0
- package/dist/delivery/infrastructure/outboxSdkAdapter.d.ts +7 -0
- package/dist/delivery/infrastructure/outboxSdkAdapter.js +164 -0
- package/dist/delivery/infrastructure/outboxSdkAdapter.js.map +1 -0
- package/dist/delivery/infrastructure/perAccountRateLimiter.d.ts +9 -0
- package/dist/delivery/infrastructure/perAccountRateLimiter.js +27 -0
- package/dist/delivery/infrastructure/perAccountRateLimiter.js.map +1 -0
- package/dist/delivery/infrastructure/smtpTransport.d.ts +43 -0
- package/dist/delivery/infrastructure/smtpTransport.js +142 -0
- package/dist/delivery/infrastructure/smtpTransport.js.map +1 -0
- package/dist/sdk/diskdClient.d.ts +1 -1
- package/dist/sdk/diskdClient.js +1 -1
- package/dist/server.js +87 -14
- package/dist/server.js.map +1 -1
- package/dist/store/driveStore.d.ts +3 -3
- package/dist/store/driveStore.js +24 -8
- package/dist/store/driveStore.js.map +1 -1
- package/dist/store/payloadTypes.d.ts +4 -0
- package/dist/sync/sync.d.ts +5 -4
- package/dist/sync/sync.js +21 -10
- package/dist/sync/sync.js.map +1 -1
- package/dist/tools/findMailboxFolder.d.ts +2 -2
- package/dist/tools/getEmail.d.ts +4 -4
- package/dist/tools/getEmails.d.ts +4 -4
- package/dist/tools/listEmails.d.ts +22 -9
- package/dist/tools/listEmails.js +37 -26
- package/dist/tools/listEmails.js.map +1 -1
- package/dist/tools/setEmailAttributes.d.ts +2 -2
- package/dist/tools/systemHydrateEmailAttachment.d.ts +4 -4
- package/dist/tools/systemHydrateEmailBodies.d.ts +8 -8
- package/package.json +5 -2
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { None, Some } from "../../domain/result.js";
|
|
2
|
+
import { decideTransportOutcome } from "../application/decideTransportOutcome.js";
|
|
3
|
+
import { decodeDeliveryProgress } from "../domain/deliveryProgress.js";
|
|
4
|
+
import { resolveDeliveryIdentity } from "../domain/identity.js";
|
|
5
|
+
import { parseEmailOutboxPayload } from "./emailOutboxPayload.js";
|
|
6
|
+
const LEASE_SECONDS = 180;
|
|
7
|
+
const RETRY_AFTER_LEASE_MS = 185_000;
|
|
8
|
+
const RETRY_AFTER_CONFLICT_MS = 15_000;
|
|
9
|
+
const MAX_DELIVERY_ATTEMPTS = 3;
|
|
10
|
+
const FAILED_UNKNOWN_REASON = "FailedUnknown: delivery started but no terminal SMTP outcome was durably recorded";
|
|
11
|
+
const repositoryErrorReason = (error) => error.kind === "InvalidResponse" || error.kind === "SdkFailure"
|
|
12
|
+
? `${error.operation} ${error.kind}: ${error.message}`
|
|
13
|
+
: `${error.operation} ${error.kind}`;
|
|
14
|
+
const retryRepositoryError = (error) => ({
|
|
15
|
+
kind: "Retry",
|
|
16
|
+
reason: repositoryErrorReason(error),
|
|
17
|
+
delayMs: RETRY_AFTER_CONFLICT_MS,
|
|
18
|
+
});
|
|
19
|
+
const receiptToJson = (receipt) => ({
|
|
20
|
+
messageId: receipt.messageId,
|
|
21
|
+
accepted: receipt.accepted,
|
|
22
|
+
rejected: receipt.rejected,
|
|
23
|
+
response: receipt.response,
|
|
24
|
+
});
|
|
25
|
+
const findConfiguredAccount = (accounts, accountId) => accounts.find((candidate) => candidate.config.name === accountId);
|
|
26
|
+
const claim = async (repository, item, leaseOwner) => repository.claim({
|
|
27
|
+
externalId: item.externalId,
|
|
28
|
+
expectedRevision: item.revision,
|
|
29
|
+
leaseOwner,
|
|
30
|
+
leaseSeconds: LEASE_SECONDS,
|
|
31
|
+
});
|
|
32
|
+
const writeFailure = async (dependencies, item, reason) => {
|
|
33
|
+
const written = await dependencies.repository.writeTerminal({
|
|
34
|
+
externalId: item.externalId,
|
|
35
|
+
expectedRevision: item.revision,
|
|
36
|
+
leaseOwner: dependencies.leaseOwner,
|
|
37
|
+
outcome: { kind: "Failed", reason },
|
|
38
|
+
});
|
|
39
|
+
return written.tag === "Err" ? retryRepositoryError(written.error) : { kind: "Ack", reason };
|
|
40
|
+
};
|
|
41
|
+
const progressOption = (item) => {
|
|
42
|
+
if (item.result.tag === "None")
|
|
43
|
+
return None;
|
|
44
|
+
const decoded = decodeDeliveryProgress(item.result.value);
|
|
45
|
+
return decoded.tag === "Ok"
|
|
46
|
+
? Some(decoded.value)
|
|
47
|
+
: Some({
|
|
48
|
+
kind: "DeliveryStarted",
|
|
49
|
+
messageId: item.externalId,
|
|
50
|
+
recordedAt: item.updatedAt,
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
const reconcileStartedDelivery = async (dependencies, item) => {
|
|
54
|
+
const claimed = await claim(dependencies.repository, item, dependencies.leaseOwner);
|
|
55
|
+
if (claimed.tag === "Err")
|
|
56
|
+
return retryRepositoryError(claimed.error);
|
|
57
|
+
return writeFailure(dependencies, claimed.value, FAILED_UNKNOWN_REASON);
|
|
58
|
+
};
|
|
59
|
+
const processOutbox = async (dependencies, item) => {
|
|
60
|
+
const progress = progressOption(item);
|
|
61
|
+
if (progress.tag === "Some" && progress.value.kind === "DeliveryStarted") {
|
|
62
|
+
return reconcileStartedDelivery(dependencies, item);
|
|
63
|
+
}
|
|
64
|
+
const payload = parseEmailOutboxPayload(item.payload, item.account);
|
|
65
|
+
const configuredAccount = findConfiguredAccount(dependencies.accounts, item.account);
|
|
66
|
+
if (payload.tag === "Err" || configuredAccount === undefined) {
|
|
67
|
+
const claimed = await claim(dependencies.repository, item, dependencies.leaseOwner);
|
|
68
|
+
if (claimed.tag === "Err")
|
|
69
|
+
return retryRepositoryError(claimed.error);
|
|
70
|
+
const reason = payload.tag === "Err"
|
|
71
|
+
? `Invalid outbound email payload: ${payload.error.issues.join("; ")}`
|
|
72
|
+
: `Delivery account not configured: ${item.account}`;
|
|
73
|
+
return writeFailure(dependencies, claimed.value, reason);
|
|
74
|
+
}
|
|
75
|
+
const identity = resolveDeliveryIdentity(dependencies.accounts.map((account) => ({
|
|
76
|
+
accountId: account.config.name,
|
|
77
|
+
allowedFromAddresses: [account.config.email],
|
|
78
|
+
})), item.account, configuredAccount.config.email);
|
|
79
|
+
if (identity.tag === "Err") {
|
|
80
|
+
const claimed = await claim(dependencies.repository, item, dependencies.leaseOwner);
|
|
81
|
+
if (claimed.tag === "Err")
|
|
82
|
+
return retryRepositoryError(claimed.error);
|
|
83
|
+
return writeFailure(dependencies, claimed.value, `Invalid delivery identity: ${identity.error.kind}`);
|
|
84
|
+
}
|
|
85
|
+
await dependencies.rateLimiter.waitForTurn(item.account);
|
|
86
|
+
const claimed = await claim(dependencies.repository, item, dependencies.leaseOwner);
|
|
87
|
+
if (claimed.tag === "Err")
|
|
88
|
+
return retryRepositoryError(claimed.error);
|
|
89
|
+
if (claimed.value.deliveryAttempts > MAX_DELIVERY_ATTEMPTS) {
|
|
90
|
+
return writeFailure(dependencies, claimed.value, "Delivery attempt limit exceeded");
|
|
91
|
+
}
|
|
92
|
+
const startedAt = dependencies.now().toISOString();
|
|
93
|
+
const marked = await dependencies.repository.writeProgress({
|
|
94
|
+
externalId: item.externalId,
|
|
95
|
+
expectedRevision: claimed.value.revision,
|
|
96
|
+
progress: {
|
|
97
|
+
kind: "DeliveryStarted",
|
|
98
|
+
messageId: payload.value.messageId,
|
|
99
|
+
recordedAt: startedAt,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
if (marked.tag === "Err")
|
|
103
|
+
return retryRepositoryError(marked.error);
|
|
104
|
+
const outcome = await configuredAccount.sender.send({
|
|
105
|
+
payload: payload.value,
|
|
106
|
+
fromAddress: identity.value.fromAddress,
|
|
107
|
+
fromName: configuredAccount.config.full_name ?? configuredAccount.config.email,
|
|
108
|
+
});
|
|
109
|
+
const decision = decideTransportOutcome(outcome);
|
|
110
|
+
if (decision.kind === "RecordSent" && outcome.kind === "Accepted") {
|
|
111
|
+
const written = await dependencies.repository.writeTerminal({
|
|
112
|
+
externalId: item.externalId,
|
|
113
|
+
expectedRevision: marked.value.revision,
|
|
114
|
+
leaseOwner: dependencies.leaseOwner,
|
|
115
|
+
outcome: { kind: "Sent", providerResponse: receiptToJson(outcome.receipt) },
|
|
116
|
+
});
|
|
117
|
+
return written.tag === "Err"
|
|
118
|
+
? retryRepositoryError(written.error)
|
|
119
|
+
: { kind: "Ack", reason: "SMTP accepted and Drive recorded Sent" };
|
|
120
|
+
}
|
|
121
|
+
if (decision.kind === "ScheduleRetry") {
|
|
122
|
+
if (marked.value.deliveryAttempts >= MAX_DELIVERY_ATTEMPTS) {
|
|
123
|
+
return writeFailure(dependencies, marked.value, `Delivery attempt limit exceeded: ${decision.reason}`);
|
|
124
|
+
}
|
|
125
|
+
const retrySafe = await dependencies.repository.writeProgress({
|
|
126
|
+
externalId: item.externalId,
|
|
127
|
+
expectedRevision: marked.value.revision,
|
|
128
|
+
progress: {
|
|
129
|
+
kind: "RetrySafe",
|
|
130
|
+
reason: decision.reason,
|
|
131
|
+
recordedAt: dependencies.now().toISOString(),
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
return retrySafe.tag === "Err"
|
|
135
|
+
? retryRepositoryError(retrySafe.error)
|
|
136
|
+
: { kind: "Retry", reason: decision.reason, delayMs: RETRY_AFTER_LEASE_MS };
|
|
137
|
+
}
|
|
138
|
+
if (decision.kind === "RecordSent") {
|
|
139
|
+
return writeFailure(dependencies, marked.value, "FailedUnknown: accepted transport decision did not include an acceptance receipt");
|
|
140
|
+
}
|
|
141
|
+
return writeFailure(dependencies, marked.value, decision.kind === "RecordFailedUnknown" ? `FailedUnknown: ${decision.reason}` : decision.reason);
|
|
142
|
+
};
|
|
143
|
+
export const buildDeliveryProcessor = (dependencies) => {
|
|
144
|
+
const inFlight = new Set();
|
|
145
|
+
return {
|
|
146
|
+
process: async (target) => {
|
|
147
|
+
const externalId = target.externalId;
|
|
148
|
+
if (inFlight.has(externalId)) {
|
|
149
|
+
return { kind: "Retry", reason: "delivery already in progress", delayMs: 5_000 };
|
|
150
|
+
}
|
|
151
|
+
inFlight.add(externalId);
|
|
152
|
+
try {
|
|
153
|
+
const loaded = await dependencies.repository.get(externalId);
|
|
154
|
+
if (loaded.tag === "Err")
|
|
155
|
+
return retryRepositoryError(loaded.error);
|
|
156
|
+
if (target.kind === "EventLocator" &&
|
|
157
|
+
(target.account !== loaded.value.account || target.mailboxId !== loaded.value.mailboxId)) {
|
|
158
|
+
return {
|
|
159
|
+
kind: "Reject",
|
|
160
|
+
reason: "event locator does not match the stored Outbox item",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
if (loaded.value.state !== "outbox") {
|
|
164
|
+
return { kind: "Ack", reason: `item is already ${loaded.value.state}` };
|
|
165
|
+
}
|
|
166
|
+
return await processOutbox(dependencies, loaded.value);
|
|
167
|
+
}
|
|
168
|
+
catch (cause) {
|
|
169
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
170
|
+
dependencies.log("delivery.processor-error", { externalId, error: reason });
|
|
171
|
+
return { kind: "Retry", reason, delayMs: RETRY_AFTER_CONFLICT_MS };
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
inFlight.delete(externalId);
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
//# sourceMappingURL=deliveryProcessor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deliveryProcessor.js","sourceRoot":"","sources":["../../../src/delivery/infrastructure/deliveryProcessor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { OutboxRepository } from "../application/outboxRepository.js";
|
|
2
|
+
import type { DeliveryLog, DeliveryProcessor } from "./deliveryProcessor.js";
|
|
3
|
+
export type DeliveryRuntime = {
|
|
4
|
+
readonly start: () => Promise<void>;
|
|
5
|
+
readonly stop: () => Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
type DeliveryRuntimeDependencies = {
|
|
8
|
+
readonly natsUrl: string;
|
|
9
|
+
readonly workspaceId: string;
|
|
10
|
+
readonly repository: OutboxRepository;
|
|
11
|
+
readonly processor: DeliveryProcessor;
|
|
12
|
+
readonly log: DeliveryLog;
|
|
13
|
+
readonly onFatal: (cause: unknown) => void;
|
|
14
|
+
};
|
|
15
|
+
export declare const deliveryConsumerName: (workspaceId: string) => string;
|
|
16
|
+
export declare const buildDeliveryRuntime: (dependencies: DeliveryRuntimeDependencies) => DeliveryRuntime;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { connect, consumerOpts, } from "nats";
|
|
3
|
+
import { None, Some } from "../../domain/result.js";
|
|
4
|
+
import { outboxCreatedSubject, parseOutboxCreatedEvent } from "./outboxEvent.js";
|
|
5
|
+
const ACK_WAIT_MS = 300_000;
|
|
6
|
+
const ACK_CONFIRM_TIMEOUT_MS = 5_000;
|
|
7
|
+
const RECONCILIATION_INTERVAL_MS = 60_000;
|
|
8
|
+
const RECONCILIATION_PAGE_SIZE = 100;
|
|
9
|
+
export const deliveryConsumerName = (workspaceId) => `email-client-mcp-${createHash("sha256").update(workspaceId, "utf8").digest("hex").slice(0, 24)}`;
|
|
10
|
+
const acknowledge = async (message, log) => {
|
|
11
|
+
const confirmed = await message.ackAck({ timeout: ACK_CONFIRM_TIMEOUT_MS });
|
|
12
|
+
if (!confirmed) {
|
|
13
|
+
log("delivery.ack-not-confirmed", { sequence: message.seq });
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const applyDisposition = async (message, disposition, log) => {
|
|
17
|
+
if (disposition.kind === "Ack") {
|
|
18
|
+
await acknowledge(message, log);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (disposition.kind === "Reject") {
|
|
22
|
+
log("delivery.event-rejected", { sequence: message.seq, reason: disposition.reason });
|
|
23
|
+
message.term();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
log("delivery.retry-scheduled", {
|
|
27
|
+
sequence: message.seq,
|
|
28
|
+
reason: disposition.reason,
|
|
29
|
+
delayMs: disposition.delayMs,
|
|
30
|
+
});
|
|
31
|
+
message.nak(disposition.delayMs);
|
|
32
|
+
};
|
|
33
|
+
const decodeEvent = (message) => {
|
|
34
|
+
const text = new TextDecoder().decode(message.data);
|
|
35
|
+
const decoded = JSON.parse(text);
|
|
36
|
+
return decoded;
|
|
37
|
+
};
|
|
38
|
+
const handleMessage = async (message, dependencies) => {
|
|
39
|
+
let decoded;
|
|
40
|
+
try {
|
|
41
|
+
decoded = decodeEvent(message);
|
|
42
|
+
}
|
|
43
|
+
catch (cause) {
|
|
44
|
+
dependencies.log("delivery.invalid-json-event", {
|
|
45
|
+
sequence: message.seq,
|
|
46
|
+
error: cause instanceof Error ? cause.message : String(cause),
|
|
47
|
+
});
|
|
48
|
+
message.term();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const event = parseOutboxCreatedEvent(decoded, dependencies.workspaceId);
|
|
52
|
+
if (event.tag === "Err") {
|
|
53
|
+
dependencies.log("delivery.invalid-outbox-event", {
|
|
54
|
+
sequence: message.seq,
|
|
55
|
+
error: event.error.kind,
|
|
56
|
+
});
|
|
57
|
+
message.term();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const disposition = await dependencies.processor.process({
|
|
61
|
+
kind: "EventLocator",
|
|
62
|
+
externalId: event.value.externalId,
|
|
63
|
+
account: event.value.account,
|
|
64
|
+
mailboxId: event.value.mailboxId,
|
|
65
|
+
});
|
|
66
|
+
dependencies.log("delivery.event-processed", {
|
|
67
|
+
eventId: event.value.eventId,
|
|
68
|
+
externalId: event.value.externalId,
|
|
69
|
+
disposition: disposition.kind,
|
|
70
|
+
});
|
|
71
|
+
await applyDisposition(message, disposition, dependencies.log);
|
|
72
|
+
};
|
|
73
|
+
const reconcilePending = async (dependencies) => {
|
|
74
|
+
let cursor = None;
|
|
75
|
+
const seenCursors = new Set();
|
|
76
|
+
for (;;) {
|
|
77
|
+
const page = await dependencies.repository.listPending({
|
|
78
|
+
limit: RECONCILIATION_PAGE_SIZE,
|
|
79
|
+
cursor,
|
|
80
|
+
});
|
|
81
|
+
if (page.tag === "Err") {
|
|
82
|
+
dependencies.log("delivery.reconciliation-list-failed", {
|
|
83
|
+
error: page.error.kind,
|
|
84
|
+
operation: page.error.operation,
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
for (const item of page.value.items) {
|
|
89
|
+
const disposition = await dependencies.processor.process({
|
|
90
|
+
kind: "Reconciliation",
|
|
91
|
+
externalId: item.externalId,
|
|
92
|
+
});
|
|
93
|
+
dependencies.log("delivery.reconciliation-item", {
|
|
94
|
+
externalId: item.externalId,
|
|
95
|
+
disposition: disposition.kind,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (page.value.nextCursor.tag === "None")
|
|
99
|
+
return;
|
|
100
|
+
if (seenCursors.has(page.value.nextCursor.value)) {
|
|
101
|
+
dependencies.log("delivery.reconciliation-cursor-cycle", {
|
|
102
|
+
cursor: page.value.nextCursor.value,
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
seenCursors.add(page.value.nextCursor.value);
|
|
107
|
+
cursor = Some(page.value.nextCursor.value);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
export const buildDeliveryRuntime = (dependencies) => {
|
|
111
|
+
let connection;
|
|
112
|
+
let subscription;
|
|
113
|
+
let consumeLoop;
|
|
114
|
+
let reconciliation;
|
|
115
|
+
let reconciliationTimer;
|
|
116
|
+
let stopping = false;
|
|
117
|
+
const beginReconciliation = () => {
|
|
118
|
+
if (reconciliation !== undefined) {
|
|
119
|
+
dependencies.log("delivery.reconciliation-skipped", { reason: "previous sweep is active" });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
reconciliation = reconcilePending(dependencies)
|
|
123
|
+
.catch((cause) => {
|
|
124
|
+
dependencies.log("delivery.reconciliation-failed", {
|
|
125
|
+
error: cause instanceof Error ? cause.message : String(cause),
|
|
126
|
+
});
|
|
127
|
+
})
|
|
128
|
+
.finally(() => {
|
|
129
|
+
reconciliation = undefined;
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
start: async () => {
|
|
134
|
+
connection = await connect({
|
|
135
|
+
servers: dependencies.natsUrl,
|
|
136
|
+
name: deliveryConsumerName(dependencies.workspaceId),
|
|
137
|
+
maxReconnectAttempts: -1,
|
|
138
|
+
});
|
|
139
|
+
const options = consumerOpts();
|
|
140
|
+
options
|
|
141
|
+
.durable(deliveryConsumerName(dependencies.workspaceId))
|
|
142
|
+
.deliverAll()
|
|
143
|
+
.ackExplicit()
|
|
144
|
+
.manualAck()
|
|
145
|
+
.ackWait(ACK_WAIT_MS)
|
|
146
|
+
.maxAckPending(1);
|
|
147
|
+
const subject = outboxCreatedSubject(dependencies.workspaceId);
|
|
148
|
+
subscription = await connection.jetstream().pullSubscribe(subject, options);
|
|
149
|
+
const activeSubscription = subscription;
|
|
150
|
+
activeSubscription.pull({ batch: 1 });
|
|
151
|
+
consumeLoop = (async () => {
|
|
152
|
+
for await (const message of activeSubscription) {
|
|
153
|
+
await handleMessage(message, dependencies);
|
|
154
|
+
if (!stopping)
|
|
155
|
+
activeSubscription.pull({ batch: 1 });
|
|
156
|
+
}
|
|
157
|
+
if (!stopping)
|
|
158
|
+
throw new Error("JetStream delivery subscription ended unexpectedly");
|
|
159
|
+
})();
|
|
160
|
+
consumeLoop.catch((cause) => {
|
|
161
|
+
if (!stopping)
|
|
162
|
+
dependencies.onFatal(cause);
|
|
163
|
+
});
|
|
164
|
+
await reconcilePending(dependencies);
|
|
165
|
+
reconciliationTimer = setInterval(beginReconciliation, RECONCILIATION_INTERVAL_MS);
|
|
166
|
+
dependencies.log("delivery.ready", {
|
|
167
|
+
subject,
|
|
168
|
+
consumer: deliveryConsumerName(dependencies.workspaceId),
|
|
169
|
+
});
|
|
170
|
+
},
|
|
171
|
+
stop: async () => {
|
|
172
|
+
stopping = true;
|
|
173
|
+
if (reconciliationTimer !== undefined)
|
|
174
|
+
clearInterval(reconciliationTimer);
|
|
175
|
+
if (reconciliation !== undefined)
|
|
176
|
+
await reconciliation;
|
|
177
|
+
if (subscription !== undefined)
|
|
178
|
+
await subscription.drain();
|
|
179
|
+
if (connection !== undefined)
|
|
180
|
+
await connection.drain();
|
|
181
|
+
if (consumeLoop !== undefined)
|
|
182
|
+
await consumeLoop;
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
};
|
|
186
|
+
//# sourceMappingURL=deliveryRuntime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deliveryRuntime.js","sourceRoot":"","sources":["../../../src/delivery/infrastructure/deliveryRuntime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,OAAO,EACP,YAAY,GAIb,MAAM,MAAM,CAAC;AACd,OAAO,EAAE,IAAI,EAAe,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAGjE,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAEjF,MAAM,WAAW,GAAG,OAAO,CAAC;AAC5B,MAAM,sBAAsB,GAAG,KAAK,CAAC;AACrC,MAAM,0BAA0B,GAAG,MAAM,CAAC;AAC1C,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAgBrC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,WAAmB,EAAU,EAAE,CAClE,oBAAoB,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AAEpG,MAAM,WAAW,GAAG,KAAK,EAAE,OAAc,EAAE,GAAgB,EAAiB,EAAE;IAC5E,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;IAC5E,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,GAAG,CAAC,4BAA4B,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,KAAK,EAC5B,OAAc,EACd,WAAgC,EAChC,GAAgB,EACD,EAAE;IACjB,IAAI,WAAW,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC/B,MAAM,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAChC,OAAO;IACT,CAAC;IACD,IAAI,WAAW,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAClC,GAAG,CAAC,yBAAyB,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;QACtF,OAAO,CAAC,IAAI,EAAE,CAAC;QACf,OAAO;IACT,CAAC;IACD,GAAG,CAAC,0BAA0B,EAAE;QAC9B,QAAQ,EAAE,OAAO,CAAC,GAAG;QACrB,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,OAAO,EAAE,WAAW,CAAC,OAAO;KAC7B,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,OAAc,EAAW,EAAE;IAC9C,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,OAAO,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1C,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,KAAK,EACzB,OAAc,EACd,YAAyC,EAC1B,EAAE;IACjB,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,YAAY,CAAC,GAAG,CAAC,6BAA6B,EAAE;YAC9C,QAAQ,EAAE,OAAO,CAAC,GAAG;YACrB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC9D,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,EAAE,CAAC;QACf,OAAO;IACT,CAAC;IACD,MAAM,KAAK,GAAG,uBAAuB,CAAC,OAAO,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;IACzE,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;QACxB,YAAY,CAAC,GAAG,CAAC,+BAA+B,EAAE;YAChD,QAAQ,EAAE,OAAO,CAAC,GAAG;YACrB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;SACxB,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,EAAE,CAAC;QACf,OAAO;IACT,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC;QACvD,IAAI,EAAE,cAAc;QACpB,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU;QAClC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO;QAC5B,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS;KACjC,CAAC,CAAC;IACH,YAAY,CAAC,GAAG,CAAC,0BAA0B,EAAE;QAC3C,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO;QAC5B,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU;QAClC,WAAW,EAAE,WAAW,CAAC,IAAI;KAC9B,CAAC,CAAC;IACH,MAAM,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC;AACjE,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,KAAK,EAAE,YAAyC,EAAiB,EAAE;IAC1F,IAAI,MAAM,GAAmB,IAAI,CAAC;IAClC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,SAAS,CAAC;QACR,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC;YACrD,KAAK,EAAE,wBAAwB;YAC/B,MAAM;SACP,CAAC,CAAC;QACH,IAAI,IAAI,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;YACvB,YAAY,CAAC,GAAG,CAAC,qCAAqC,EAAE;gBACtD,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;gBACtB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS;aAChC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACpC,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC;gBACvD,IAAI,EAAE,gBAAgB;gBACtB,UAAU,EAAE,IAAI,CAAC,UAAU;aAC5B,CAAC,CAAC;YACH,YAAY,CAAC,GAAG,CAAC,8BAA8B,EAAE;gBAC/C,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,WAAW,EAAE,WAAW,CAAC,IAAI;aAC9B,CAAC,CAAC;QACL,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,KAAK,MAAM;YAAE,OAAO;QACjD,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,YAAY,CAAC,GAAG,CAAC,sCAAsC,EAAE;gBACvD,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK;aACpC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,YAAyC,EACxB,EAAE;IACnB,IAAI,UAAsC,CAAC;IAC3C,IAAI,YAAmD,CAAC;IACxD,IAAI,WAAsC,CAAC;IAC3C,IAAI,cAAyC,CAAC;IAC9C,IAAI,mBAA+C,CAAC;IACpD,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,mBAAmB,GAAG,GAAS,EAAE;QACrC,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YACjC,YAAY,CAAC,GAAG,CAAC,iCAAiC,EAAE,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAC,CAAC;YAC5F,OAAO;QACT,CAAC;QACD,cAAc,GAAG,gBAAgB,CAAC,YAAY,CAAC;aAC5C,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,YAAY,CAAC,GAAG,CAAC,gCAAgC,EAAE;gBACjD,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC9D,CAAC,CAAC;QACL,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,cAAc,GAAG,SAAS,CAAC;QAC7B,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,UAAU,GAAG,MAAM,OAAO,CAAC;gBACzB,OAAO,EAAE,YAAY,CAAC,OAAO;gBAC7B,IAAI,EAAE,oBAAoB,CAAC,YAAY,CAAC,WAAW,CAAC;gBACpD,oBAAoB,EAAE,CAAC,CAAC;aACzB,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,EAAE,CAAC;YAC/B,OAAO;iBACJ,OAAO,CAAC,oBAAoB,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;iBACvD,UAAU,EAAE;iBACZ,WAAW,EAAE;iBACb,SAAS,EAAE;iBACX,OAAO,CAAC,WAAW,CAAC;iBACpB,aAAa,CAAC,CAAC,CAAC,CAAC;YACpB,MAAM,OAAO,GAAG,oBAAoB,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YAC/D,YAAY,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC5E,MAAM,kBAAkB,GAAG,YAAY,CAAC;YACxC,kBAAkB,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YACtC,WAAW,GAAG,CAAC,KAAK,IAAI,EAAE;gBACxB,IAAI,KAAK,EAAE,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;oBAC/C,MAAM,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;oBAC3C,IAAI,CAAC,QAAQ;wBAAE,kBAAkB,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;gBACvD,CAAC;gBACD,IAAI,CAAC,QAAQ;oBAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;YACvF,CAAC,CAAC,EAAE,CAAC;YACL,WAAW,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBAC1B,IAAI,CAAC,QAAQ;oBAAE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7C,CAAC,CAAC,CAAC;YAEH,MAAM,gBAAgB,CAAC,YAAY,CAAC,CAAC;YACrC,mBAAmB,GAAG,WAAW,CAAC,mBAAmB,EAAE,0BAA0B,CAAC,CAAC;YACnF,YAAY,CAAC,GAAG,CAAC,gBAAgB,EAAE;gBACjC,OAAO;gBACP,QAAQ,EAAE,oBAAoB,CAAC,YAAY,CAAC,WAAW,CAAC;aACzD,CAAC,CAAC;QACL,CAAC;QACD,IAAI,EAAE,KAAK,IAAI,EAAE;YACf,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,mBAAmB,KAAK,SAAS;gBAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC;YAC1E,IAAI,cAAc,KAAK,SAAS;gBAAE,MAAM,cAAc,CAAC;YACvD,IAAI,YAAY,KAAK,SAAS;gBAAE,MAAM,YAAY,CAAC,KAAK,EAAE,CAAC;YAC3D,IAAI,UAAU,KAAK,SAAS;gBAAE,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;YACvD,IAAI,WAAW,KAAK,SAAS;gBAAE,MAAM,WAAW,CAAC;QACnD,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EmailOutboxPayload } from "@diskd-ai/sdk";
|
|
2
|
+
import { type Result } from "../../domain/result.js";
|
|
3
|
+
export type EmailOutboxPayloadError = {
|
|
4
|
+
readonly kind: "InvalidEmailOutboxPayload";
|
|
5
|
+
readonly issues: readonly string[];
|
|
6
|
+
};
|
|
7
|
+
export declare const parseEmailOutboxPayload: (rawPayload: unknown, expectedAccount: string) => Result<EmailOutboxPayloadError, EmailOutboxPayload>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Err, Ok } from "../../domain/result.js";
|
|
3
|
+
const senderContactSchema = z.object({
|
|
4
|
+
name: z.string(),
|
|
5
|
+
address: z.string().min(1),
|
|
6
|
+
});
|
|
7
|
+
const recipientContactSchema = z.object({
|
|
8
|
+
name: z.string(),
|
|
9
|
+
address: z.string().email(),
|
|
10
|
+
});
|
|
11
|
+
const emailOutboxPayloadSchema = z.object({
|
|
12
|
+
messageId: z.string().min(1),
|
|
13
|
+
account: z.string().min(1),
|
|
14
|
+
threadId: z.string().min(1).nullable(),
|
|
15
|
+
inReplyTo: z.string().min(1).nullable(),
|
|
16
|
+
from: senderContactSchema,
|
|
17
|
+
to: z.array(recipientContactSchema).min(1),
|
|
18
|
+
cc: z.array(recipientContactSchema),
|
|
19
|
+
bcc: z.array(recipientContactSchema),
|
|
20
|
+
subject: z.string(),
|
|
21
|
+
bodyText: z.string(),
|
|
22
|
+
bodyHtml: z.string(),
|
|
23
|
+
hasAttachments: z.literal(false),
|
|
24
|
+
attachments: z.tuple([]),
|
|
25
|
+
});
|
|
26
|
+
export const parseEmailOutboxPayload = (rawPayload, expectedAccount) => {
|
|
27
|
+
const parsed = emailOutboxPayloadSchema.safeParse(rawPayload);
|
|
28
|
+
if (!parsed.success) {
|
|
29
|
+
return Err({
|
|
30
|
+
kind: "InvalidEmailOutboxPayload",
|
|
31
|
+
issues: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (parsed.data.account !== expectedAccount) {
|
|
35
|
+
return Err({
|
|
36
|
+
kind: "InvalidEmailOutboxPayload",
|
|
37
|
+
issues: ["account: payload account does not match the stored Outbox account"],
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return Ok(parsed.data);
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=emailOutboxPayload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emailOutboxPayload.js","sourceRoot":"","sources":["../../../src/delivery/infrastructure/emailOutboxPayload.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,MAAM,wBAAwB,CAAC;AAE9D,MAAM,mBAAmB,GAAkC,CAAC,CAAC,MAAM,CAAC;IAClE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC3B,CAAC,CAAC;AAEH,MAAM,sBAAsB,GAAkC,CAAC,CAAC,MAAM,CAAC;IACrE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;CAC5B,CAAC,CAAC;AAEH,MAAM,wBAAwB,GAAkC,CAAC,CAAC,MAAM,CAAC;IACvE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACtC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvC,IAAI,EAAE,mBAAmB;IACzB,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1C,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC;IACnC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC;IACpC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IAChC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;CACzB,CAAC,CAAC;AAOH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,UAAmB,EACnB,eAAuB,EAC8B,EAAE;IACvD,MAAM,MAAM,GAAG,wBAAwB,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAC9D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,GAAG,CAAC;YACT,IAAI,EAAE,2BAA2B;YACjC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;SACxF,CAAC,CAAC;IACL,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,KAAK,eAAe,EAAE,CAAC;QAC5C,OAAO,GAAG,CAAC;YACT,IAAI,EAAE,2BAA2B;YACjC,MAAM,EAAE,CAAC,mEAAmE,CAAC;SAC9E,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type Result } from "../../domain/result.js";
|
|
2
|
+
export declare const outboxCreatedSubject: (workspaceId: string) => string;
|
|
3
|
+
export type OutboxCreatedEvent = {
|
|
4
|
+
readonly eventId: string;
|
|
5
|
+
readonly workspaceId: string;
|
|
6
|
+
readonly occurredAt: string;
|
|
7
|
+
readonly account: string;
|
|
8
|
+
readonly mailboxId: string;
|
|
9
|
+
readonly externalId: string;
|
|
10
|
+
readonly revision: string;
|
|
11
|
+
};
|
|
12
|
+
export type OutboxEventError = {
|
|
13
|
+
readonly kind: "InvalidEvent";
|
|
14
|
+
readonly issues: readonly string[];
|
|
15
|
+
} | {
|
|
16
|
+
readonly kind: "WrongWorkspace";
|
|
17
|
+
readonly expectedWorkspaceId: string;
|
|
18
|
+
readonly actualWorkspaceId: string;
|
|
19
|
+
} | {
|
|
20
|
+
readonly kind: "EventIdMismatch";
|
|
21
|
+
readonly expectedEventId: string;
|
|
22
|
+
readonly actualEventId: string;
|
|
23
|
+
};
|
|
24
|
+
export declare const createOutboxEventId: (input: {
|
|
25
|
+
readonly workspaceId: string;
|
|
26
|
+
readonly mailboxId: string;
|
|
27
|
+
readonly externalId: string;
|
|
28
|
+
readonly revision: string;
|
|
29
|
+
}) => string;
|
|
30
|
+
export declare const parseOutboxCreatedEvent: (rawEvent: unknown, expectedWorkspaceId: string) => Result<OutboxEventError, OutboxCreatedEvent>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { Err, Ok } from "../../domain/result.js";
|
|
4
|
+
import { isValidMailboxId } from "../../store/conventions.js";
|
|
5
|
+
const OUTBOX_CREATED_TYPE = "exchange.outbox.created";
|
|
6
|
+
export const outboxCreatedSubject = (workspaceId) => `platform.${workspaceId}.${OUTBOX_CREATED_TYPE}`;
|
|
7
|
+
const outboxEventDataSchema = z
|
|
8
|
+
.object({
|
|
9
|
+
account: z.string().min(1),
|
|
10
|
+
mailboxId: z.string().refine(isValidMailboxId, "invalid mailboxId"),
|
|
11
|
+
externalId: z.string().min(1),
|
|
12
|
+
revision: z.string().min(1),
|
|
13
|
+
})
|
|
14
|
+
.strict();
|
|
15
|
+
const outboxCreatedEnvelopeSchema = z
|
|
16
|
+
.object({
|
|
17
|
+
id: z.string().regex(/^[a-f0-9]{64}$/),
|
|
18
|
+
type: z.literal(OUTBOX_CREATED_TYPE),
|
|
19
|
+
workspace_id: z.string().min(1),
|
|
20
|
+
timestamp: z.string().datetime({ offset: true }),
|
|
21
|
+
version: z.literal(1),
|
|
22
|
+
data: outboxEventDataSchema,
|
|
23
|
+
})
|
|
24
|
+
.strict();
|
|
25
|
+
export const createOutboxEventId = (input) => createHash("sha256")
|
|
26
|
+
.update([
|
|
27
|
+
input.workspaceId,
|
|
28
|
+
input.mailboxId,
|
|
29
|
+
input.externalId,
|
|
30
|
+
input.revision,
|
|
31
|
+
OUTBOX_CREATED_TYPE,
|
|
32
|
+
].join("\n"), "utf8")
|
|
33
|
+
.digest("hex");
|
|
34
|
+
export const parseOutboxCreatedEvent = (rawEvent, expectedWorkspaceId) => {
|
|
35
|
+
const parsed = outboxCreatedEnvelopeSchema.safeParse(rawEvent);
|
|
36
|
+
if (!parsed.success) {
|
|
37
|
+
return Err({
|
|
38
|
+
kind: "InvalidEvent",
|
|
39
|
+
issues: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (parsed.data.workspace_id !== expectedWorkspaceId) {
|
|
43
|
+
return Err({
|
|
44
|
+
kind: "WrongWorkspace",
|
|
45
|
+
expectedWorkspaceId,
|
|
46
|
+
actualWorkspaceId: parsed.data.workspace_id,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const expectedEventId = createOutboxEventId({
|
|
50
|
+
workspaceId: parsed.data.workspace_id,
|
|
51
|
+
mailboxId: parsed.data.data.mailboxId,
|
|
52
|
+
externalId: parsed.data.data.externalId,
|
|
53
|
+
revision: parsed.data.data.revision,
|
|
54
|
+
});
|
|
55
|
+
if (parsed.data.id !== expectedEventId) {
|
|
56
|
+
return Err({
|
|
57
|
+
kind: "EventIdMismatch",
|
|
58
|
+
expectedEventId,
|
|
59
|
+
actualEventId: parsed.data.id,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return Ok({
|
|
63
|
+
eventId: parsed.data.id,
|
|
64
|
+
workspaceId: parsed.data.workspace_id,
|
|
65
|
+
occurredAt: parsed.data.timestamp,
|
|
66
|
+
account: parsed.data.data.account,
|
|
67
|
+
mailboxId: parsed.data.data.mailboxId,
|
|
68
|
+
externalId: parsed.data.data.externalId,
|
|
69
|
+
revision: parsed.data.data.revision,
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=outboxEvent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outboxEvent.js","sourceRoot":"","sources":["../../../src/delivery/infrastructure/outboxEvent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MessagesStoreClient } from "@diskd-ai/sdk";
|
|
2
|
+
import { type Result } from "../../domain/result.js";
|
|
3
|
+
import type { OutboxItem, OutboxRepository, OutboxRepositoryError, OutboxRepositoryOperation } from "../application/outboxRepository.js";
|
|
4
|
+
type OutboxSdkBoundary = Pick<MessagesStoreClient, "exchange" | "outbox">;
|
|
5
|
+
export declare const parseSdkOutboxItem: (operation: OutboxRepositoryOperation, rawItem: unknown) => Result<OutboxRepositoryError, OutboxItem>;
|
|
6
|
+
export declare const buildOutboxRepository: (sdk: OutboxSdkBoundary) => OutboxRepository;
|
|
7
|
+
export {};
|