@remit/web-client 0.0.201 → 0.0.202

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/web-client",
3
- "version": "0.0.201",
3
+ "version": "0.0.202",
4
4
  "type": "module",
5
5
  "description": "Remit web client, published as composable primitives — the app shell, auth shells, and runtime config. A distributor imports what it composes and bundles it.",
6
6
  "exports": {
@@ -285,3 +285,20 @@ describe("initialStage", () => {
285
285
  assert.equal(initialStage(3), "choose-fate");
286
286
  });
287
287
  });
288
+
289
+ describe("hasChildFolders in a flat namespace", () => {
290
+ it("does not read a shared prefix as a child", () => {
291
+ const target = folder({
292
+ mailboxId: "work",
293
+ fullPath: "Work",
294
+ hierarchyDelimiter: "",
295
+ });
296
+ assert.equal(
297
+ hasChildFolders(target, [
298
+ target,
299
+ { mailboxId: "shop", fullPath: "Workshop" },
300
+ ]),
301
+ false,
302
+ );
303
+ });
304
+ });
@@ -34,12 +34,14 @@ const isInboxPath = (fullPath: string): boolean =>
34
34
  /**
35
35
  * A folder has children when another mailbox's path is nested under it — the
36
36
  * folder's own path followed by the hierarchy delimiter. The delimiter matters:
37
- * `Work` is not a parent of `Workshop`, only of `Work/Shop`.
37
+ * `Work` is not a parent of `Workshop`, only of `Work/Shop`. A server that
38
+ * reports no delimiter has a flat namespace, where nothing nests at all.
38
39
  */
39
40
  export const hasChildFolders = (
40
41
  folder: Pick<FolderNode, "mailboxId" | "fullPath" | "hierarchyDelimiter">,
41
42
  all: readonly Pick<FolderNode, "mailboxId" | "fullPath">[],
42
43
  ): boolean => {
44
+ if (folder.hierarchyDelimiter.length === 0) return false;
43
45
  const prefix = `${folder.fullPath}${folder.hierarchyDelimiter}`;
44
46
  return all.some(
45
47
  (other) =>
@@ -107,3 +107,21 @@ describe("validateNewFolderName", () => {
107
107
  );
108
108
  });
109
109
  });
110
+
111
+ describe("validateNewFolderName in a flat namespace", () => {
112
+ const flat = { delimiter: "", existingPaths: ["INBOX", "Archive"] };
113
+
114
+ it("accepts a name when the server reports no delimiter", () => {
115
+ assert.equal(
116
+ validateNewFolderName({ ...flat, name: "Receipts" }),
117
+ undefined,
118
+ );
119
+ });
120
+
121
+ it("still catches a collision", () => {
122
+ assert.match(
123
+ validateNewFolderName({ ...flat, name: "inbox" }) ?? "",
124
+ /already exists/,
125
+ );
126
+ });
127
+ });
@@ -19,7 +19,7 @@ export function composeFolderPath(name: string, parent?: FolderTarget): string {
19
19
  * match `inbox/x` against `INBOX/x` without folding case anywhere else.
20
20
  */
21
21
  const canonicalizePath = (path: string, delimiter: string): string => {
22
- const parts = path.split(delimiter);
22
+ const parts = delimiter.length === 0 ? [path] : path.split(delimiter);
23
23
  if (parts[0]?.toLowerCase() === "inbox") parts[0] = "INBOX";
24
24
  return parts.join(delimiter);
25
25
  };
@@ -29,7 +29,8 @@ const canonicalizePath = (path: string, delimiter: string): string => {
29
29
  * name must be non-empty, must not contain the hierarchy delimiter (nesting is
30
30
  * expressed by the parent select, not by typing a path), and must not collide
31
31
  * with an existing folder — INBOX compared case-insensitively, everything else
32
- * exactly.
32
+ * exactly. A server that reports no delimiter has a flat namespace, so there is
33
+ * no separator to forbid and every name would otherwise contain it.
33
34
  */
34
35
  export function validateNewFolderName({
35
36
  name,
@@ -44,7 +45,7 @@ export function validateNewFolderName({
44
45
  }): string | undefined {
45
46
  const trimmed = name.trim();
46
47
  if (trimmed === "") return "Enter a folder name.";
47
- if (trimmed.includes(delimiter))
48
+ if (delimiter.length > 0 && trimmed.includes(delimiter))
48
49
  return `A folder name can't contain "${delimiter}".`;
49
50
  const candidate = canonicalizePath(
50
51
  composeFolderPath(trimmed, parent),