@diskd-ai/email-client-mcp 0.1.38 → 0.1.40

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 (62) hide show
  1. package/dist/config/schema.d.ts +525 -5
  2. package/dist/config/schema.js +67 -4
  3. package/dist/config/schema.js.map +1 -1
  4. package/dist/delivery/application/decideTransportOutcome.d.ts +15 -0
  5. package/dist/delivery/application/decideTransportOutcome.js +14 -0
  6. package/dist/delivery/application/decideTransportOutcome.js.map +1 -0
  7. package/dist/delivery/application/outboxRepository.d.ts +83 -0
  8. package/dist/delivery/application/outboxRepository.js +2 -0
  9. package/dist/delivery/application/outboxRepository.js.map +1 -0
  10. package/dist/delivery/domain/deliveryProgress.d.ts +17 -0
  11. package/dist/delivery/domain/deliveryProgress.js +34 -0
  12. package/dist/delivery/domain/deliveryProgress.js.map +1 -0
  13. package/dist/delivery/domain/deliveryState.d.ts +21 -0
  14. package/dist/delivery/domain/deliveryState.js +7 -0
  15. package/dist/delivery/domain/deliveryState.js.map +1 -0
  16. package/dist/delivery/domain/identity.d.ts +28 -0
  17. package/dist/delivery/domain/identity.js +28 -0
  18. package/dist/delivery/domain/identity.js.map +1 -0
  19. package/dist/delivery/domain/json.d.ts +5 -0
  20. package/dist/delivery/domain/json.js +2 -0
  21. package/dist/delivery/domain/json.js.map +1 -0
  22. package/dist/delivery/domain/transportOutcome.d.ts +31 -0
  23. package/dist/delivery/domain/transportOutcome.js +2 -0
  24. package/dist/delivery/domain/transportOutcome.js.map +1 -0
  25. package/dist/delivery/infrastructure/deliveryProcessor.d.ts +42 -0
  26. package/dist/delivery/infrastructure/deliveryProcessor.js +179 -0
  27. package/dist/delivery/infrastructure/deliveryProcessor.js.map +1 -0
  28. package/dist/delivery/infrastructure/deliveryRuntime.d.ts +13 -0
  29. package/dist/delivery/infrastructure/deliveryRuntime.js +80 -0
  30. package/dist/delivery/infrastructure/deliveryRuntime.js.map +1 -0
  31. package/dist/delivery/infrastructure/emailOutboxPayload.d.ts +7 -0
  32. package/dist/delivery/infrastructure/emailOutboxPayload.js +42 -0
  33. package/dist/delivery/infrastructure/emailOutboxPayload.js.map +1 -0
  34. package/dist/delivery/infrastructure/outboxSdkAdapter.d.ts +7 -0
  35. package/dist/delivery/infrastructure/outboxSdkAdapter.js +164 -0
  36. package/dist/delivery/infrastructure/outboxSdkAdapter.js.map +1 -0
  37. package/dist/delivery/infrastructure/perAccountRateLimiter.d.ts +9 -0
  38. package/dist/delivery/infrastructure/perAccountRateLimiter.js +27 -0
  39. package/dist/delivery/infrastructure/perAccountRateLimiter.js.map +1 -0
  40. package/dist/delivery/infrastructure/smtpTransport.d.ts +43 -0
  41. package/dist/delivery/infrastructure/smtpTransport.js +142 -0
  42. package/dist/delivery/infrastructure/smtpTransport.js.map +1 -0
  43. package/dist/events/platformEventNotifier.d.ts +22 -0
  44. package/dist/events/platformEventNotifier.js +29 -0
  45. package/dist/events/platformEventNotifier.js.map +1 -0
  46. package/dist/sdk/diskdClient.d.ts +1 -1
  47. package/dist/sdk/diskdClient.js +1 -1
  48. package/dist/server.js +88 -47
  49. package/dist/server.js.map +1 -1
  50. package/dist/store/driveStore.d.ts +2 -2
  51. package/dist/store/payloadTypes.d.ts +4 -0
  52. package/dist/sync/sync.d.ts +3 -2
  53. package/dist/sync/sync.js +10 -5
  54. package/dist/sync/sync.js.map +1 -1
  55. package/dist/tools/findMailboxFolder.d.ts +2 -2
  56. package/dist/tools/getEmail.d.ts +4 -4
  57. package/dist/tools/getEmails.d.ts +4 -4
  58. package/dist/tools/listEmails.d.ts +9 -9
  59. package/dist/tools/setEmailAttributes.d.ts +2 -2
  60. package/dist/tools/systemHydrateEmailAttachment.d.ts +4 -4
  61. package/dist/tools/systemHydrateEmailBodies.d.ts +8 -8
  62. package/package.json +4 -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,13 @@
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 repository: OutboxRepository;
9
+ readonly processor: DeliveryProcessor;
10
+ readonly log: DeliveryLog;
11
+ };
12
+ export declare const buildDeliveryRuntime: (dependencies: DeliveryRuntimeDependencies) => DeliveryRuntime;
13
+ export {};
@@ -0,0 +1,80 @@
1
+ import { None, Some } from "../../domain/result.js";
2
+ const RECONCILIATION_INTERVAL_MS = 60_000;
3
+ const RECONCILIATION_PAGE_SIZE = 100;
4
+ const reconcilePending = async (dependencies) => {
5
+ let cursor = None;
6
+ const seenCursors = new Set();
7
+ for (;;) {
8
+ const page = await dependencies.repository.listPending({
9
+ limit: RECONCILIATION_PAGE_SIZE,
10
+ cursor,
11
+ });
12
+ if (page.tag === "Err") {
13
+ dependencies.log("delivery.reconciliation-list-failed", {
14
+ error: page.error.kind,
15
+ operation: page.error.operation,
16
+ });
17
+ return;
18
+ }
19
+ for (const item of page.value.items) {
20
+ const disposition = await dependencies.processor.process({
21
+ kind: "Reconciliation",
22
+ externalId: item.externalId,
23
+ });
24
+ dependencies.log("delivery.reconciliation-item", {
25
+ externalId: item.externalId,
26
+ disposition: disposition.kind,
27
+ });
28
+ }
29
+ if (page.value.nextCursor.tag === "None")
30
+ return;
31
+ if (seenCursors.has(page.value.nextCursor.value)) {
32
+ dependencies.log("delivery.reconciliation-cursor-cycle", {
33
+ cursor: page.value.nextCursor.value,
34
+ });
35
+ return;
36
+ }
37
+ seenCursors.add(page.value.nextCursor.value);
38
+ cursor = Some(page.value.nextCursor.value);
39
+ }
40
+ };
41
+ export const buildDeliveryRuntime = (dependencies) => {
42
+ let reconciliation;
43
+ let reconciliationTimer;
44
+ let stopping = false;
45
+ const beginReconciliation = () => {
46
+ if (stopping)
47
+ return;
48
+ if (reconciliation !== undefined) {
49
+ dependencies.log("delivery.reconciliation-skipped", { reason: "previous sweep is active" });
50
+ return;
51
+ }
52
+ reconciliation = reconcilePending(dependencies)
53
+ .catch((cause) => {
54
+ dependencies.log("delivery.reconciliation-failed", {
55
+ error: cause instanceof Error ? cause.message : String(cause),
56
+ });
57
+ })
58
+ .finally(() => {
59
+ reconciliation = undefined;
60
+ });
61
+ };
62
+ return {
63
+ start: async () => {
64
+ await reconcilePending(dependencies);
65
+ reconciliationTimer = setInterval(beginReconciliation, RECONCILIATION_INTERVAL_MS);
66
+ dependencies.log("delivery.ready", {
67
+ source: "drive-outbox-reconciliation",
68
+ intervalMs: RECONCILIATION_INTERVAL_MS,
69
+ });
70
+ },
71
+ stop: async () => {
72
+ stopping = true;
73
+ if (reconciliationTimer !== undefined)
74
+ clearInterval(reconciliationTimer);
75
+ if (reconciliation !== undefined)
76
+ await reconciliation;
77
+ },
78
+ };
79
+ };
80
+ //# sourceMappingURL=deliveryRuntime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deliveryRuntime.js","sourceRoot":"","sources":["../../../src/delivery/infrastructure/deliveryRuntime.ts"],"names":[],"mappings":"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"}
@@ -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,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 {};
@@ -0,0 +1,164 @@
1
+ import { z } from "zod";
2
+ import { Err, None, Ok, Some } from "../../domain/result.js";
3
+ import { isValidMailboxId } from "../../store/conventions.js";
4
+ import { encodeDeliveryProgress } from "../domain/deliveryProgress.js";
5
+ const exchangeStateSchema = z.enum([
6
+ "review",
7
+ "outbox",
8
+ "sent",
9
+ "failed",
10
+ "reconciliation_required",
11
+ ]);
12
+ const jsonValueSchema = z.lazy(() => z.union([
13
+ z.string(),
14
+ z.number().finite(),
15
+ z.boolean(),
16
+ z.null(),
17
+ z.array(jsonValueSchema),
18
+ z.record(jsonValueSchema),
19
+ ]));
20
+ const jsonObjectSchema = z.record(jsonValueSchema);
21
+ const sdkOutboxItemSchema = z
22
+ .object({
23
+ externalId: z.string().min(1),
24
+ account: z.string().min(1),
25
+ mailboxId: z.string().refine(isValidMailboxId, "invalid mailboxId"),
26
+ state: exchangeStateSchema,
27
+ payload: jsonObjectSchema,
28
+ result: jsonObjectSchema.nullable(),
29
+ revision: z.string().min(1),
30
+ deliveryAttempts: z.number().int().min(0),
31
+ leaseOwner: z.string().min(1).nullable(),
32
+ leaseExpiresAt: z.string().datetime({ offset: true }).nullable(),
33
+ failureReason: z.string().min(1).nullable(),
34
+ createdAt: z.string().datetime({ offset: true }),
35
+ updatedAt: z.string().datetime({ offset: true }),
36
+ })
37
+ .strict();
38
+ const sdkOutboxPageSchema = z
39
+ .object({
40
+ items: z.array(z.unknown()),
41
+ nextCursor: z.string().min(1).nullable(),
42
+ })
43
+ .strict();
44
+ const toOption = (value) => (value === null ? None : Some(value));
45
+ const invalidResponse = (operation, message) => ({ kind: "InvalidResponse", operation, message });
46
+ export const parseSdkOutboxItem = (operation, rawItem) => {
47
+ const parsed = sdkOutboxItemSchema.safeParse(rawItem);
48
+ if (!parsed.success) {
49
+ return Err(invalidResponse(operation, parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")));
50
+ }
51
+ let lease;
52
+ if (parsed.data.leaseOwner === null && parsed.data.leaseExpiresAt === null) {
53
+ lease = { kind: "Available" };
54
+ }
55
+ else if (parsed.data.leaseOwner !== null && parsed.data.leaseExpiresAt !== null) {
56
+ lease = {
57
+ kind: "Leased",
58
+ owner: parsed.data.leaseOwner,
59
+ expiresAt: parsed.data.leaseExpiresAt,
60
+ };
61
+ }
62
+ else {
63
+ return Err(invalidResponse(operation, "lease owner and expiry must be present together"));
64
+ }
65
+ if ((parsed.data.state === "sent" || parsed.data.state === "failed") &&
66
+ lease.kind !== "Available") {
67
+ return Err(invalidResponse(operation, "terminal item must not retain a lease"));
68
+ }
69
+ if (parsed.data.state === "failed" && parsed.data.failureReason === null) {
70
+ return Err(invalidResponse(operation, "failed item must include a failure reason"));
71
+ }
72
+ return Ok({
73
+ externalId: parsed.data.externalId,
74
+ account: parsed.data.account,
75
+ mailboxId: parsed.data.mailboxId,
76
+ state: parsed.data.state,
77
+ payload: parsed.data.payload,
78
+ result: toOption(parsed.data.result),
79
+ revision: parsed.data.revision,
80
+ deliveryAttempts: parsed.data.deliveryAttempts,
81
+ lease,
82
+ failureReason: toOption(parsed.data.failureReason),
83
+ createdAt: parsed.data.createdAt,
84
+ updatedAt: parsed.data.updatedAt,
85
+ });
86
+ };
87
+ const causeMessage = (cause) => {
88
+ if (cause instanceof Error)
89
+ return cause.message;
90
+ try {
91
+ return String(cause);
92
+ }
93
+ catch {
94
+ return "unprintable SDK failure";
95
+ }
96
+ };
97
+ const classifySdkFailure = (operation, cause) => {
98
+ const message = causeMessage(cause);
99
+ if (/\bREVISION_CONFLICT\b/.test(message))
100
+ return { kind: "RevisionConflict", operation };
101
+ if (/\bLEASE_CONFLICT\b/.test(message))
102
+ return { kind: "LeaseConflict", operation };
103
+ if (/\bLEASE_EXPIRED\b/.test(message))
104
+ return { kind: "LeaseExpired", operation };
105
+ if (/\bNOT_FOUND\b/.test(message))
106
+ return { kind: "NotFound", operation };
107
+ if (/Invalid messages_store response/.test(message)) {
108
+ return invalidResponse(operation, message);
109
+ }
110
+ return { kind: "SdkFailure", operation, message };
111
+ };
112
+ const runItemOperation = async (operation, call) => {
113
+ try {
114
+ return parseSdkOutboxItem(operation, await call());
115
+ }
116
+ catch (cause) {
117
+ return Err(classifySdkFailure(operation, cause));
118
+ }
119
+ };
120
+ const toSdkTerminalOutcome = (outcome) => outcome.kind === "Sent"
121
+ ? { state: "sent", providerResponse: outcome.providerResponse }
122
+ : { state: "failed", reason: outcome.reason };
123
+ export const buildOutboxRepository = (sdk) => ({
124
+ get: (externalId) => runItemOperation("Get", () => sdk.outbox.get({ externalId })),
125
+ listPending: async (input) => {
126
+ try {
127
+ const rawPage = await sdk.outbox.listPending({
128
+ limit: input.limit,
129
+ ...(input.cursor.tag === "Some" ? { cursor: input.cursor.value } : {}),
130
+ });
131
+ const parsedPage = sdkOutboxPageSchema.safeParse(rawPage);
132
+ if (!parsedPage.success) {
133
+ return Err(invalidResponse("ListPending", parsedPage.error.issues
134
+ .map((issue) => `${issue.path.join(".")}: ${issue.message}`)
135
+ .join("; ")));
136
+ }
137
+ const parsedItems = parsedPage.data.items.map((item) => parseSdkOutboxItem("ListPending", item));
138
+ const failedItem = parsedItems.find((item) => item.tag === "Err");
139
+ if (failedItem !== undefined && failedItem.tag === "Err")
140
+ return failedItem;
141
+ return Ok({
142
+ items: parsedItems.flatMap((item) => (item.tag === "Ok" ? [item.value] : [])),
143
+ nextCursor: toOption(parsedPage.data.nextCursor),
144
+ });
145
+ }
146
+ catch (cause) {
147
+ return Err(classifySdkFailure("ListPending", cause));
148
+ }
149
+ },
150
+ claim: (input) => runItemOperation("Claim", () => sdk.outbox.claim(input)),
151
+ renewLease: (input) => runItemOperation("RenewLease", () => sdk.outbox.renewLease(input)),
152
+ writeProgress: (input) => runItemOperation("WriteProgress", () => sdk.exchange.update({
153
+ externalId: input.externalId,
154
+ expectedRevision: input.expectedRevision,
155
+ patch: { result: encodeDeliveryProgress(input.progress) },
156
+ })),
157
+ writeTerminal: (input) => runItemOperation("WriteTerminal", () => sdk.outbox.writeTerminal({
158
+ externalId: input.externalId,
159
+ expectedRevision: input.expectedRevision,
160
+ leaseOwner: input.leaseOwner,
161
+ outcome: toSdkTerminalOutcome(input.outcome),
162
+ })),
163
+ });
164
+ //# sourceMappingURL=outboxSdkAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outboxSdkAdapter.js","sourceRoot":"","sources":["../../../src/delivery/infrastructure/outboxSdkAdapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export type PerAccountRateLimiter = {
2
+ readonly waitForTurn: (accountId: string) => Promise<void>;
3
+ };
4
+ type RateLimiterDependencies = {
5
+ readonly nowMs: () => number;
6
+ readonly sleep: (milliseconds: number) => Promise<void>;
7
+ };
8
+ export declare const buildPerAccountRateLimiter: (ratePerMinute: number, dependencies?: RateLimiterDependencies) => PerAccountRateLimiter;
9
+ export {};
@@ -0,0 +1,27 @@
1
+ const defaultDependencies = {
2
+ nowMs: () => Date.now(),
3
+ sleep: (milliseconds) => new Promise((resolve) => {
4
+ setTimeout(resolve, milliseconds);
5
+ }),
6
+ };
7
+ export const buildPerAccountRateLimiter = (ratePerMinute, dependencies = defaultDependencies) => {
8
+ const intervalMs = Math.ceil(60_000 / ratePerMinute);
9
+ const nextAllowedByAccount = new Map();
10
+ const tails = new Map();
11
+ return {
12
+ waitForTurn: async (accountId) => {
13
+ const prior = tails.get(accountId) ?? Promise.resolve();
14
+ const turn = prior.then(async () => {
15
+ const waitMs = Math.max(0, (nextAllowedByAccount.get(accountId) ?? dependencies.nowMs()) - dependencies.nowMs());
16
+ if (waitMs > 0)
17
+ await dependencies.sleep(waitMs);
18
+ nextAllowedByAccount.set(accountId, dependencies.nowMs() + intervalMs);
19
+ });
20
+ tails.set(accountId, turn);
21
+ await turn;
22
+ if (tails.get(accountId) === turn)
23
+ tails.delete(accountId);
24
+ },
25
+ };
26
+ };
27
+ //# sourceMappingURL=perAccountRateLimiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perAccountRateLimiter.js","sourceRoot":"","sources":["../../../src/delivery/infrastructure/perAccountRateLimiter.ts"],"names":[],"mappings":"AASA,MAAM,mBAAmB,GAA4B;IACnD,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;IACvB,KAAK,EAAE,CAAC,YAAY,EAAE,EAAE,CACtB,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACtB,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACpC,CAAC,CAAC;CACL,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,aAAqB,EACrB,eAAwC,mBAAmB,EACpC,EAAE;IACzB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC,CAAC;IACrD,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE/C,OAAO;QACL,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;YAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACxD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;gBACjC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CACrB,CAAC,EACD,CAAC,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,YAAY,CAAC,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC,KAAK,EAAE,CACrF,CAAC;gBACF,IAAI,MAAM,GAAG,CAAC;oBAAE,MAAM,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBACjD,oBAAoB,CAAC,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,KAAK,EAAE,GAAG,UAAU,CAAC,CAAC;YACzE,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAC3B,MAAM,IAAI,CAAC;YACX,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,IAAI;gBAAE,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC7D,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { EmailOutboxPayload } from "@diskd-ai/sdk";
2
+ import { type Account, type SmtpSettings } from "../../config/schema.js";
3
+ import type { TransportOutcome } from "../domain/transportOutcome.js";
4
+ type PasswordSmtpAuth = {
5
+ readonly type: "login";
6
+ readonly user: string;
7
+ readonly pass: string;
8
+ };
9
+ type OAuthSmtpAuth = {
10
+ readonly type: "OAuth2";
11
+ readonly user: string;
12
+ readonly clientId: string;
13
+ readonly clientSecret: string;
14
+ readonly refreshToken: string;
15
+ };
16
+ export type SmtpConnectionOptions = {
17
+ readonly host: string;
18
+ readonly port: number;
19
+ readonly secure: boolean;
20
+ readonly requireTLS: boolean;
21
+ readonly tls: {
22
+ readonly rejectUnauthorized: boolean;
23
+ };
24
+ readonly connectionTimeout: number;
25
+ readonly greetingTimeout: number;
26
+ readonly socketTimeout: number;
27
+ readonly auth: PasswordSmtpAuth | OAuthSmtpAuth;
28
+ };
29
+ export type SmtpMessage = {
30
+ readonly payload: EmailOutboxPayload;
31
+ readonly fromAddress: string;
32
+ readonly fromName: string;
33
+ };
34
+ export type SmtpSender = {
35
+ readonly send: (message: SmtpMessage) => Promise<TransportOutcome>;
36
+ readonly close: () => void;
37
+ };
38
+ /** Classify only failures proven to occur before SMTP acceptance as retryable. */
39
+ export declare const classifySmtpFailure: (cause: unknown) => TransportOutcome;
40
+ export declare const buildSmtpConnectionOptions: (account: Account, smtp: SmtpSettings) => SmtpConnectionOptions;
41
+ export declare const toSmtpMessageId: (messageId: string) => string;
42
+ export declare const buildSmtpSender: (account: Account) => SmtpSender;
43
+ export {};