@remit/backend 0.0.112 → 0.0.113

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/backend",
3
- "version": "0.0.112",
3
+ "version": "0.0.113",
4
4
  "description": "Remit Mail Inspector API backend",
5
5
  "license": "MIT",
6
6
  "author": "",
@@ -8,6 +8,7 @@ import {
8
8
  type CanonicalMailboxRoleValue,
9
9
  composeFolderRoleAppointmentLabelName,
10
10
  } from "@remit/data-ports/folder-role";
11
+ import { rebaseMailboxPath } from "@remit/data-ports/mailbox-name";
11
12
  import { MailboxSyncStatus, MessageSystemFlag } from "@remit/domain-enums";
12
13
  import type { APIGatewayProxyEvent } from "aws-lambda";
13
14
  import { getAccountConfigIdFromEvent } from "../auth.js";
@@ -76,24 +77,6 @@ export interface MailboxPatchClient {
76
77
  accountSetting: Pick<IAccountSettingRepository, "get" | "upsert" | "delete">;
77
78
  }
78
79
 
79
- /**
80
- * Where a recorded path lands after the rename, or `undefined` when the rename
81
- * did not move it. IMAP RENAME moves the whole subtree in one command and
82
- * `renameChildPaths` rewrites every descendant row with it, so a label under
83
- * the renamed branch moves exactly as far as its prefix does.
84
- */
85
- const rebasePath = (
86
- recorded: string,
87
- oldPath: string,
88
- newPath: string,
89
- delimiter: string,
90
- ): string | undefined => {
91
- if (recorded === oldPath) return newPath;
92
- const branch = `${oldPath}${delimiter}`;
93
- if (!recorded.startsWith(branch)) return undefined;
94
- return `${newPath}${recorded.slice(oldPath.length)}`;
95
- };
96
-
97
80
  /**
98
81
  * A reader-side rename keeps every mailboxId, so the appointments survive it —
99
82
  * but the paths recorded beside them (#887) would still name where the folders
@@ -101,8 +84,8 @@ const rebasePath = (
101
84
  * names a path the user has not seen since the rename.
102
85
  *
103
86
  * The renamed folder is matched by id; its descendants are matched by the path
104
- * each label already holds, which is the path their rows carried until this
105
- * rename rewrote them.
87
+ * each label already holds, which is the path their rows carried until the
88
+ * rename intent rewrote them.
106
89
  */
107
90
  const refreshAppointmentLabels = async (
108
91
  accountSetting: Pick<IAccountSettingRepository, "get" | "upsert">,
@@ -122,7 +105,7 @@ const refreshAppointmentLabels = async (
122
105
  ? renamed.newPath
123
106
  : appointment.lastKnownPath === undefined
124
107
  ? undefined
125
- : rebasePath(
108
+ : rebaseMailboxPath(
126
109
  appointment.lastKnownPath,
127
110
  renamed.oldPath,
128
111
  renamed.newPath,
@@ -144,8 +127,8 @@ const refreshAppointmentLabels = async (
144
127
  * Apply a mailbox PATCH body: override changes first (mute flag + display-name/
145
128
  * role overrides — written to per-mailbox AccountSetting rows, no IMAP
146
129
  * machinery), then rename — which triggers the IMAP rename machinery
147
- * (syncStatus/oldPath + MAILBOX_RENAME event) — only when `fullPath` is
148
- * present. An override-only PATCH therefore never calls
130
+ * (the recorded subtree intent + MAILBOX_RENAME event) — only when `fullPath`
131
+ * is present. An override-only PATCH therefore never calls
149
132
  * `mailboxQueue.renameMailbox`.
150
133
  */
151
134
  export const applyMailboxPatch = async (
@@ -160,8 +143,8 @@ export const applyMailboxPatch = async (
160
143
  // --- Override settings (mute flag + display-name/role overrides) ---
161
144
  // Written to per-mailbox AccountSetting rows (RFC 032) with the same
162
145
  // null→remove semantics as UpdateAddressFlagsInput. Applied before (and
163
- // independent of) any rename so an override-only PATCH never touches
164
- // syncStatus/oldPath.
146
+ // independent of) any rename so an override-only PATCH never touches the
147
+ // row's mutation state.
165
148
  const changes = pickMailboxOverrideChanges(body);
166
149
  if (Object.keys(changes).length > 0) {
167
150
  await applyMailboxOverrideChanges(
@@ -268,6 +251,11 @@ const toMailboxResponse = (
268
251
  highWaterMarkUid: mailbox.highWaterMarkUid,
269
252
  lastMessageSyncAt: mailbox.lastMessageSyncAt,
270
253
  syncStatus: mailbox.syncStatus,
254
+ // The row is the only thing that knows what a rename is aiming at, and
255
+ // `fullPath` stays the confirmed one throughout (D2), so without this a
256
+ // client cannot tell a create in flight from a rename in flight, name the
257
+ // target of a failed rename, or offer the right retry.
258
+ pendingPath: mailbox.pendingPath,
271
259
  muted: overrides.muted,
272
260
  displayNameOverride: overrides.displayNameOverride,
273
261
  createdAt: mailbox.createdAt,