@remit/data-ports 0.0.85 → 0.0.87

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/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { deriveFilterTtl } from "./filter-ttl.js";
2
2
  export type { IAccountRepository } from "./interfaces/account.js";
3
3
  export type { IAccountConfigRepository } from "./interfaces/account-config.js";
4
4
  export type { IAccountExportRequestRepository } from "./interfaces/account-export-request.js";
5
+ export type { AccountDataPurgeEvent, AccountDeleteEvent, AccountExportEvent, AccountFanoutEvent, OrganizeJobEvent, SenderCategoryBackApplyEvent, } from "./interfaces/account-fanout-events.js";
5
6
  export type { IAccountSettingRepository } from "./interfaces/account-setting.js";
6
7
  export type { IAddressRepository } from "./interfaces/address.js";
7
8
  export type { ICalendarCollectionRepository } from "./interfaces/calendar-collection.js";
@@ -28,6 +29,7 @@ export { holdsRoom } from "./interfaces/outbox-attachment.js";
28
29
  export type { IOutboxMessageRepository } from "./interfaces/outbox-message.js";
29
30
  export { APPENDED_UID_NONE, APPENDED_UID_UNREPORTED, isSentCopyFiled, } from "./interfaces/outbox-message.js";
30
31
  export type { IQuarantineRepository } from "./interfaces/quarantine.js";
32
+ export { type FifoQueueMessage, type FifoQueueProducer, type GroupedQueueMessage, type GroupedQueueProducer, QUEUE_BATCH_LIMIT, type QueueBatchResult, type QueueKind, type QueueMessage, type QueueProducer, type QueueProducerOf, } from "./interfaces/queue.js";
31
33
  export type { ISenderSignerStandingRepository } from "./interfaces/sender-signer-standing.js";
32
34
  export type { ISyncWarningRepository, SurfacedSyncWarnings, } from "./interfaces/sync-warning.js";
33
35
  export type { IThreadMessageRepository } from "./interfaces/thread-message.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { deriveFilterTtl } from "./filter-ttl.js";
2
2
  export { holdsRoom } from "./interfaces/outbox-attachment.js";
3
3
  export { APPENDED_UID_NONE, APPENDED_UID_UNREPORTED, isSentCopyFiled, } from "./interfaces/outbox-message.js";
4
+ export { QUEUE_BATCH_LIMIT, } from "./interfaces/queue.js";
4
5
  export { abandonedMutationOf, hasAbandonedDelete, hasAbandonedMove, } from "./message-settlement.js";
5
6
  export { FILTER_NO_ACTION, isSenderMuted, isSenderMuteFilter, } from "./sender-mute.js";
@@ -0,0 +1,58 @@
1
+ import type { ThreadMessageItem } from "../types.js";
2
+ export type AccountDeleteEvent = {
3
+ type: "AccountDelete";
4
+ accountConfigId: string;
5
+ };
6
+ /**
7
+ * Per-account hard-delete. Purges ONE mail account's data (mailboxes,
8
+ * messages, body parts, thread messages, S3 objects) while
9
+ * keeping the AccountConfig/tenant and any sibling accounts intact. Emitted
10
+ * by the API on `deleteAccount`; consumed by the fanout worker.
11
+ */
12
+ export type AccountDataPurgeEvent = {
13
+ type: "AccountDataPurge";
14
+ accountId: string;
15
+ accountConfigId: string;
16
+ };
17
+ export type AccountExportEvent = {
18
+ type: "AccountExport";
19
+ accountConfigId: string;
20
+ accountExportRequestId: string;
21
+ };
22
+ /**
23
+ * A "all like these" back-apply job (RFC 034, #1278). Rides the same
24
+ * account-fanout queue as export: the API writes a Pending OrganizeJobRequest
25
+ * row and enqueues this; the fanout worker matches the corpus and applies the
26
+ * action, driving the row to Complete/Failed. Never creates a Filter row.
27
+ */
28
+ export type OrganizeJobEvent = {
29
+ type: "OrganizeJob";
30
+ accountConfigId: string;
31
+ organizeJobId: string;
32
+ };
33
+ /**
34
+ * Retroactive half of a sender-category override (#415, RFC 039 Decision 3).
35
+ * Rides the same account-fanout queue as the organize back-apply: the API
36
+ * enqueues one when `Address.flags.category` is SET, and the fanout worker
37
+ * re-labels a bounded recent batch of that sender's already-classified mail.
38
+ * A CLEAR never enqueues one: reverting to auto-classification says nothing
39
+ * about what the classifier would have answered, and re-deriving it would need
40
+ * every message's body back.
41
+ *
42
+ * `category` and `categorySetAt` together name WHICH set this job was fired
43
+ * for, and the worker refuses a job whose set no longer stands on the Address.
44
+ * That check is what the standard queue's semantics make necessary rather than
45
+ * merely tidy: delivery is at-least-once and unordered, so without it a job
46
+ * that fails and redelivers after the user has corrected the category re-labels
47
+ * those 500 messages back to the superseded one, leaving the flag and the mail
48
+ * permanently disagreeing with nothing to reconcile them.
49
+ */
50
+ export type SenderCategoryBackApplyEvent = {
51
+ type: "SenderCategoryBackApply";
52
+ accountConfigId: string;
53
+ addressId: string;
54
+ normalizedEmail: string;
55
+ category: ThreadMessageItem["category"];
56
+ categorySetAt: number;
57
+ };
58
+ export type AccountFanoutEvent = AccountDeleteEvent | AccountDataPurgeEvent | AccountExportEvent | OrganizeJobEvent | SenderCategoryBackApplyEvent;
@@ -0,0 +1 @@
1
+ export {};
@@ -11,6 +11,13 @@ export interface IMessageRepository {
11
11
  update(messageId: string, input: UpdateMessageInput): Promise<MessageItem>;
12
12
  clearBodyStorageKey(messageId: string): Promise<MessageItem>;
13
13
  clearSpamReport(messageId: string): Promise<MessageItem>;
14
+ setSearchText(messageId: string, text: string): Promise<void>;
15
+ copySearchText(sourceMessageId: string, targetMessageId: string): Promise<void>;
16
+ syncSearch(messageIds: string[]): Promise<void>;
17
+ listMissingSearchText(after: string, limit: number): Promise<Array<{
18
+ messageId: string;
19
+ bodyStorageKey: string;
20
+ }>>;
14
21
  delete(messageId: string): Promise<void>;
15
22
  deleteMany(messageIds: string[]): Promise<void>;
16
23
  /**
@@ -0,0 +1,22 @@
1
+ export interface QueueMessage<T> {
2
+ readonly body: T;
3
+ }
4
+ export interface GroupedQueueMessage<T> extends QueueMessage<T> {
5
+ readonly groupId: string;
6
+ }
7
+ export interface FifoQueueMessage<T> extends GroupedQueueMessage<T> {
8
+ readonly deduplicationId: string;
9
+ }
10
+ export interface QueueBatchResult {
11
+ readonly refused: ReadonlySet<number>;
12
+ }
13
+ export declare const QUEUE_BATCH_LIMIT = 10;
14
+ export type QueueKind = "Standard" | "Grouped" | "Fifo";
15
+ export interface QueueProducerOf<K extends QueueKind, M> {
16
+ readonly kind: K;
17
+ readonly send: (message: M) => Promise<void>;
18
+ readonly sendBatch: (messages: readonly M[]) => Promise<QueueBatchResult>;
19
+ }
20
+ export type QueueProducer<T> = QueueProducerOf<"Standard", QueueMessage<T>>;
21
+ export type GroupedQueueProducer<T> = QueueProducerOf<"Grouped", GroupedQueueMessage<T>>;
22
+ export type FifoQueueProducer<T> = QueueProducerOf<"Fifo", FifoQueueMessage<T>>;
@@ -0,0 +1,2 @@
1
+ // The smallest batch every queue backend accepts (SQS caps a batch at ten).
2
+ export const QUEUE_BATCH_LIMIT = 10;
@@ -0,0 +1,15 @@
1
+ import type { FifoQueueProducer, GroupedQueueProducer, QueueProducer } from "./interfaces/queue.js";
2
+ export interface RecordedMessage {
3
+ body: Record<string, unknown>;
4
+ groupId: string | undefined;
5
+ deduplicationId: string | undefined;
6
+ }
7
+ export interface RecordingQueue extends QueueProducer<unknown> {
8
+ readonly fifo: FifoQueueProducer<unknown>;
9
+ readonly grouped: GroupedQueueProducer<unknown>;
10
+ readonly sent: RecordedMessage[];
11
+ readonly answer: (next: () => Promise<unknown>) => void;
12
+ readonly refuse: (positions: readonly number[]) => void;
13
+ }
14
+ export declare const recordingQueue: () => RecordingQueue;
15
+ export declare const failingQueue: (error: unknown) => RecordingQueue;
@@ -0,0 +1,50 @@
1
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2
+ const stringField = (message, field) => {
3
+ const value = Reflect.get(message, field);
4
+ return typeof value === "string" ? value : undefined;
5
+ };
6
+ const recorded = (message) => {
7
+ const body = JSON.parse(JSON.stringify(message.body));
8
+ if (!isRecord(body))
9
+ throw new Error("A queued body must be an object");
10
+ return {
11
+ body,
12
+ groupId: stringField(message, "groupId"),
13
+ deduplicationId: stringField(message, "deduplicationId"),
14
+ };
15
+ };
16
+ export const recordingQueue = () => {
17
+ const sent = [];
18
+ let answer = async () => ({});
19
+ let refused = new Set();
20
+ const send = async (message) => {
21
+ sent.push(recorded(message));
22
+ await answer();
23
+ };
24
+ const sendBatch = async (messages) => {
25
+ sent.push(...messages.map(recorded));
26
+ await answer();
27
+ return { refused };
28
+ };
29
+ return {
30
+ kind: "Standard",
31
+ fifo: { kind: "Fifo", send, sendBatch },
32
+ grouped: { kind: "Grouped", send, sendBatch },
33
+ sent,
34
+ answer: (next) => {
35
+ answer = next;
36
+ },
37
+ refuse: (positions) => {
38
+ refused = new Set(positions);
39
+ },
40
+ send,
41
+ sendBatch,
42
+ };
43
+ };
44
+ export const failingQueue = (error) => {
45
+ const queue = recordingQueue();
46
+ queue.answer(async () => {
47
+ throw error;
48
+ });
49
+ return queue;
50
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/data-ports",
3
- "version": "0.0.85",
3
+ "version": "0.0.87",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -26,11 +26,13 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@remit/domain-enums": "*",
29
- "short-uuid": "*",
30
- "uuid": "*",
31
- "zod": "^3.25.0",
29
+ "short-uuid": "^6.0.3",
30
+ "uuid": "^13.0.2",
32
31
  "@remit/api-zod-schemas": "*"
33
32
  },
33
+ "peerDependencies": {
34
+ "zod": "^3.25.0"
35
+ },
34
36
  "license": "MIT",
35
37
  "publishConfig": {
36
38
  "access": "public"