@ai-matrx/vault 0.1.21 → 0.1.22

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/CHANGELOG.md CHANGED
@@ -1,9 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.22
4
+
5
+ - **A member's credential shared into an organization is the organization's, never "Personal".** An item in an organization's list is the viewer's own only when the viewer owns it (`platform.list_my_organizations` `user_id`, else the owner of the person's own list); a member's own credential shared into the organization is attributed to that organization (`VaultOwner.memberShared`), counted there, its badge says "A member's own credential, shared into <org>", and Share / Delete show disabled with the reason (`manageUnavailableReason`, `ownerDetail`); Copy stays offered to an editor. Consumer action: none.
6
+ - **The person's own items show first.** `VaultPort.load(onProgress?)` hands partial snapshots (`VaultSnapshot.pending`): the own list and the type catalog open the vault, each organization's list streams in as it answers; the list's count line says "loading <org>…" (no layout jump). A Refresh still applies only the finished read. Consumer action: none (a host with its own `VaultPort` may ignore `onProgress`).
7
+ - **The compact Vault and Show pickers keep a real width** (6rem minimum) at split widths (640–1119px): the Authenticator button beside them is icon-only (its name in the tooltip). Consumer action: none.
8
+ - **The star is on every list row** — the row's hover quick actions and its More / right-click menu (`ItemList` `favorites`, `onToggleFavorite`). Consumer action: none.
9
+ - **Every organization stays in the vault switcher**, empty ones too (count 0); an empty organization opens on "Nothing here yet" with New item saving there. Consumer action: none.
10
+ - **A host's own right-click menu reads the row's entries**: `rowMenuEntriesAt(target)` returns the clicked row's entries (`RowMenuEntry.kind` `copy` / `favorite` / `open` / `copy-name` / `host`) — the same list the row's own menu renders. **Consumer action:** web app (`rowContextMenu: "host"`): add the `copy` and `favorite` entries of `rowMenuEntriesAt(target)` to its v3 menu. Extension and desktop: none.
11
+
3
12
  ## 0.1.21
4
13
 
5
- UNVERIFIED: this release has no Unreleased entry and the source comparison could not be made (the caller supplied no source comparison). Whether source changed is unknown; consumer action is unknown.
6
- Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/vault/src`.
14
+ SOURCE CHANGED WITH NO RELEASE NOTE. This release shipped source changes that nobody described in an Unreleased entry,
15
+ so they may include breaking API changes. Consumer action is UNKNOWN: read the diff before upgrading.
16
+ Diff: `git diff 8efbb129f3 4abc64ee28 -- apps/shared/vault/src`
17
+
18
+ Changed shipped source files (1):
19
+ - `apps/shared/vault/src/FieldRows.tsx`
7
20
 
8
21
  ## 0.1.20
9
22
 
package/dist/index.cjs CHANGED
@@ -160,6 +160,7 @@ __export(src_exports, {
160
160
  needsRecentSignIn: () => needsRecentSignIn,
161
161
  parseDefinitions: () => parseDefinitions,
162
162
  providerOf: () => providerOf,
163
+ rowMenuEntriesAt: () => rowMenuEntriesAt,
163
164
  siteDomainOf: () => siteDomainOf,
164
165
  sortItems: () => sortItems,
165
166
  useConfirmed: () => useConfirmed,
@@ -918,6 +919,19 @@ var import_format3 = require("@ai-matrx/kit/format");
918
919
  var import_format = require("@ai-matrx/kit/format");
919
920
 
920
921
  // src/model.ts
922
+ function manageUnavailableReason(item) {
923
+ if (item.capabilities.canManage) return null;
924
+ if (item.owner.memberShared) return "Only the member who owns it can share, move or delete it";
925
+ if (item.owner.kind === "shared") return "Only its owner can share, move or delete it";
926
+ if (item.owner.kind === "organization") return "Only this organization's vault admins can change that";
927
+ return "You can use this item but not share, move or delete it";
928
+ }
929
+ function ownerDetail(owner) {
930
+ if (owner.memberShared) return `A member's own credential, shared into ${owner.label}`;
931
+ if (owner.kind === "organization") return `Organization: ${owner.label}`;
932
+ if (owner.kind === "shared") return "Shared with you by its owner";
933
+ return "Your own vault";
934
+ }
921
935
  var DEFAULT_LOCATION = { vault: "all", view: "all", q: "", sort: "name", itemId: null, mode: "view", kind: null, url: "", panel: null };
922
936
  var SORTS = ["name", "name-desc", "website", "updated", "created", "viewed"];
923
937
  function locationFromParams(params, itemId = params.get("item")) {
@@ -6057,6 +6071,7 @@ function ItemPane(props) {
6057
6071
  const setValue = (key, v) => setDraft((d) => ({ ...d, values: { ...d.values, [key]: v } }));
6058
6072
  const canEdit = item ? item.capabilities.canEdit || item.capabilities.canManage : true;
6059
6073
  const canManage = item?.capabilities.canManage ?? false;
6074
+ const manageReason = item ? manageUnavailableReason(item) : null;
6060
6075
  async function save() {
6061
6076
  if (!definition && !item) return;
6062
6077
  setBusy(true);
@@ -6203,7 +6218,8 @@ function ItemPane(props) {
6203
6218
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h2", { className: "truncate text-base font-semibold leading-tight", children: editing ? draft.name || (mode === "new" ? `New ${typeLabel.toLowerCase()}` : item?.name) : item?.name }),
6204
6219
  /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "flex min-w-0 items-center gap-1 text-xs text-muted-foreground", children: [
6205
6220
  typeLabel ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "truncate", children: typeLabel }) : null,
6206
- shownOwner ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Badge, { className: "shrink-0", icon: shownOwner.glyph, "aria-label": `Saved in ${shownOwner.label}`, children: shownOwner.label }) : null,
6221
+ shownOwner ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Badge, { className: "shrink-0", icon: shownOwner.glyph, "aria-label": `Saved in ${shownOwner.label}`, title: item && mode !== "new" ? ownerDetail(item.owner) : void 0, children: shownOwner.label }) : null,
6222
+ item && mode !== "new" && item.owner.memberShared ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "shrink-0", "data-member-shared": "", children: "\xB7 A member\u2019s own" }) : null,
6207
6223
  item && !editing ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SharingSummary, { item, port, className: "hidden truncate before:mr-1 before:content-['\xB7'] sm:inline" }, `${item.updatedAt}-${refreshKey ?? ""}`) : null
6208
6224
  ] })
6209
6225
  ] }),
@@ -6241,9 +6257,9 @@ function ItemPane(props) {
6241
6257
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ShareGlyph, { className: "size-4" }),
6242
6258
  "Share"
6243
6259
  ] }) : null,
6244
- canManage ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_design_system17.DropdownMenuItem, { onSelect: () => onPanel("move"), children: [
6260
+ canManage || item.capabilities.canEdit ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_design_system17.DropdownMenuItem, { onSelect: () => onPanel("move"), children: [
6245
6261
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MoveGlyph, { className: "size-4" }),
6246
- "Move or copy"
6262
+ canManage ? "Move or copy" : "Copy to\u2026"
6247
6263
  ] }) : null,
6248
6264
  item.capabilities.canReveal ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_design_system17.DropdownMenuItem, { onSelect: () => onPanel("send"), children: [
6249
6265
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SendGlyph, { className: "size-4" }),
@@ -6267,6 +6283,17 @@ function ItemPane(props) {
6267
6283
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(TrashGlyph, { className: "size-4" }),
6268
6284
  "Delete"
6269
6285
  ] })
6286
+ ] }) : manageReason ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
6287
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_design_system17.DropdownMenuSeparator, {}),
6288
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_design_system17.DropdownMenuItem, { disabled: true, children: [
6289
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ShareGlyph, { className: "size-4" }),
6290
+ "Share"
6291
+ ] }),
6292
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_design_system17.DropdownMenuItem, { disabled: true, children: [
6293
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(TrashGlyph, { className: "size-4" }),
6294
+ "Delete"
6295
+ ] }),
6296
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "max-w-64 px-2 py-1.5 text-xs text-muted-foreground", "data-manage-reason": "", children: manageReason })
6270
6297
  ] }) : null
6271
6298
  ] })
6272
6299
  ] })
@@ -6497,6 +6524,17 @@ function ItemPane(props) {
6497
6524
  ] }) });
6498
6525
  }
6499
6526
 
6527
+ // src/row-menu.ts
6528
+ var readers = /* @__PURE__ */ new WeakMap();
6529
+ function registerRowMenu(el, read) {
6530
+ if (el) readers.set(el, read);
6531
+ }
6532
+ function rowMenuEntriesAt(target) {
6533
+ const row = target?.closest("[data-item]");
6534
+ const read = row ? readers.get(row) : void 0;
6535
+ return read ? read() : [];
6536
+ }
6537
+
6500
6538
  // src/ItemList.tsx
6501
6539
  var import_jsx_runtime28 = require("react/jsx-runtime");
6502
6540
  var RECOVERY_KEYS = /^recovery_codes?$/;
@@ -6518,7 +6556,7 @@ function subtitleOf(item, withOwner) {
6518
6556
  const what = loginSiteOf(item) || item.definitionLabel;
6519
6557
  return withOwner ? `${what} \xB7 ${item.owner.label}` : what;
6520
6558
  }
6521
- function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, showOwner, canSortViewed = false, onRefresh, refreshing = false, rowContextMenu = "package", rowMenuItems }) {
6559
+ function ItemList({ title, items, definitions, port, selectedId, onSelect, query, onQuery, sort, onSort, onNew, matchingIds, header, showOwner, canSortViewed = false, onRefresh, refreshing = false, rowContextMenu = "package", rowMenuItems, stillLoading, favorites, onToggleFavorite }) {
6522
6560
  const sorts = SORTS2.filter((s) => canSortViewed || s.value !== "viewed");
6523
6561
  const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
6524
6562
  const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
@@ -6539,7 +6577,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6539
6577
  }
6540
6578
  };
6541
6579
  const mixedOwners = showOwner ?? new Set(items.map((i) => i.owner.organizationId ?? i.owner.kind)).size > 1;
6542
- const row = (i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ListRow, { item: i, family: familyOf2(i.definitionKey), port, on: i.id === selectedId, onSelect, withOwner: mixedOwners, ownContextMenu: rowContextMenu === "package", extra: rowMenuItems }, i.id);
6580
+ const row = (i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ListRow, { item: i, family: familyOf2(i.definitionKey), port, on: i.id === selectedId, onSelect, withOwner: mixedOwners, ownContextMenu: rowContextMenu === "package", extra: rowMenuItems, favorite: onToggleFavorite ? { on: favorites?.has(i.id) ?? false, toggle: () => onToggleFavorite(i.id) } : void 0 }, i.id);
6543
6581
  const sortLabel = sorts.find((s) => s.value === sort)?.label ?? "Name A\u2013Z";
6544
6582
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("section", { "aria-label": title, className: "flex h-full min-h-0 flex-col border-r border-border bg-background", children: [
6545
6583
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children: [
@@ -6549,10 +6587,18 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6549
6587
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(PlusGlyph, {}), "aria-label": "New item", onClick: onNew })
6550
6588
  ] }),
6551
6589
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center justify-between gap-2 px-1", children: [
6552
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "truncate text-xs text-muted-foreground", children: [
6590
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "flex min-w-0 items-center gap-1.5 truncate text-xs text-muted-foreground", role: "status", "aria-live": "polite", children: [
6553
6591
  items.length,
6554
6592
  " ",
6555
- items.length === 1 ? "item" : "items"
6593
+ items.length === 1 ? "item" : "items",
6594
+ stillLoading && stillLoading.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "flex min-w-0 items-center gap-1 truncate", title: `Still loading ${stillLoading.join(", ")}`, "data-still-loading": "", children: [
6595
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(RefreshGlyph, { className: "size-3 shrink-0 animate-spin" }),
6596
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "truncate", children: [
6597
+ "loading ",
6598
+ stillLoading.length === 1 ? stillLoading[0] : `${stillLoading.length} more`,
6599
+ "\u2026"
6600
+ ] })
6601
+ ] }) : null
6556
6602
  ] }),
6557
6603
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "flex shrink-0 items-center gap-0.5", children: [
6558
6604
  onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(RefreshGlyph, { className: refreshing ? "animate-spin" : "" }), "aria-label": "Refresh", title: "Refresh the list", disabled: refreshing, onClick: onRefresh }) : null,
@@ -6564,7 +6610,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6564
6610
  ] })
6565
6611
  ] }),
6566
6612
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto px-2 pb-3", onKeyDown, children: items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col items-center gap-3 px-4 py-12 text-center", children: [
6567
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-sm text-muted-foreground", children: query ? `Nothing matches \u201C${query}\u201D.` : "Nothing here yet." }),
6613
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-sm text-muted-foreground", children: query ? `Nothing matches \u201C${query}\u201D.` : stillLoading && stillLoading.length > 0 ? "Still loading\u2026" : "Nothing here yet." }),
6568
6614
  query ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "outline", onClick: () => onQuery(""), children: "Clear search" }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(PlusGlyph, {}), onClick: onNew, children: "New item" })
6569
6615
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("ul", { ref: listRef, className: "flex flex-col gap-px", children: [
6570
6616
  pinned.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("li", { className: "px-2 pb-1 pt-1 text-xs font-medium text-muted-foreground", children: "This page" }) : null,
@@ -6574,7 +6620,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6574
6620
  ] }) })
6575
6621
  ] });
6576
6622
  }
6577
- function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra }) {
6623
+ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra, favorite }) {
6578
6624
  const confirmed = useConfirmed();
6579
6625
  const copy = useCopy();
6580
6626
  const notify = useNotify();
@@ -6602,9 +6648,32 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6602
6648
  const others = otherCopyFields(item, user, secret);
6603
6649
  const custom = canUse ? item.custom.filter((c) => c.value) : [];
6604
6650
  const hostEntries = extra?.(item) ?? [];
6651
+ const copyEntries = [
6652
+ ...user ? [{ id: "copy:username", kind: "copy", label: `Copy ${user.label.toLowerCase()}`, onSelect: copyUser }] : [],
6653
+ ...secret ? [{ id: "copy:password", kind: "copy", label: "Copy password", onSelect: copyPassword }] : [],
6654
+ ...code ? [{ id: "copy:code", kind: "copy", label: "Copy one-time code", onSelect: copyCode }] : [],
6655
+ ...others.map((f) => ({ id: `copy:${f.key}`, kind: "copy", label: `Copy ${f.label.toLowerCase()}`, onSelect: () => void run(f.label, () => port.reveal(item, f.key), f.handling !== "visible") })),
6656
+ ...custom.map((c) => ({ id: `copy:custom:${c.key}`, kind: "copy", label: `Copy ${c.label.toLowerCase()}`, onSelect: () => void copy(c.value, c.label, { secret: false }) }))
6657
+ ];
6658
+ const favoriteEntry = favorite ? { id: "favorite", kind: "favorite", label: favorite.on ? "Remove from favorites" : "Add to favorites", onSelect: favorite.toggle } : null;
6659
+ const entries = [
6660
+ ...copyEntries,
6661
+ ...favoriteEntry ? [favoriteEntry] : [],
6662
+ { id: "open", kind: "open", label: "Open", onSelect: () => onSelect(item.id) },
6663
+ { id: "copy-name", kind: "copy-name", label: "Copy name", onSelect: copyName },
6664
+ ...hostEntries.map((h) => ({ ...h, kind: h.kind ?? "host" }))
6665
+ ];
6666
+ const entriesRef = (0, import_react29.useRef)(entries);
6667
+ entriesRef.current = entries;
6668
+ const glyphOf = (e) => e.id === "copy:username" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, { className: "size-4" }) : e.id === "copy:password" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, { className: "size-4" }) : e.id === "copy:code" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ClockGlyph, { className: "size-4" }) : e.kind === "favorite" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(StarGlyph, { className: (0, import_design_system20.cn)("size-4", favorite?.on && "fill-current text-warning") }) : e.kind === "open" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRightGlyph, { className: "size-4" }) : e.kind === "host" ? null : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" });
6669
+ const menuItem = (e) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { disabled: e.disabled ?? false, onSelect: e.onSelect, children: [
6670
+ glyphOf(e),
6671
+ e.label
6672
+ ] }, e.id ?? e.label);
6605
6673
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
6606
6674
  "li",
6607
6675
  {
6676
+ ref: (el) => registerRowMenu(el, () => entriesRef.current),
6608
6677
  className: "group relative",
6609
6678
  "data-item": item.id,
6610
6679
  onContextMenu: ownContextMenu ? (e) => {
@@ -6630,7 +6699,8 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6630
6699
  }
6631
6700
  ),
6632
6701
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "absolute right-1.5 top-1/2 flex -translate-y-1/2 items-center gap-0.5", "data-surface": on ? "primary" : void 0, children: [
6633
- user || secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: (0, import_design_system20.cn)("hidden items-center gap-0.5 rounded-md pl-2 [@media(hover:hover)]:group-hover:flex [@media(hover:hover)]:group-focus-within:flex", on ? "bg-primary" : "bg-accent"), "data-row-quick": "", children: [
6702
+ user || secret || favorite ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: (0, import_design_system20.cn)("hidden items-center gap-0.5 rounded-md pl-2 [@media(hover:hover)]:group-hover:flex [@media(hover:hover)]:group-focus-within:flex", on ? "bg-primary" : "bg-accent"), "data-row-quick": "", children: [
6703
+ favorite ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(StarGlyph, { className: favorite.on ? "fill-current text-warning" : "" }), "aria-label": favorite.on ? "Remove from favorites" : "Add to favorites", "aria-pressed": favorite.on, onClick: favorite.toggle }) : null,
6634
6704
  user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, {}), "aria-label": `Copy ${user.label}`, disabled: busy, onClick: copyUser }) : null,
6635
6705
  secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, {}), "aria-label": "Copy password", disabled: busy, onClick: copyPassword }) : null
6636
6706
  ] }) : null,
@@ -6646,40 +6716,11 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6646
6716
  }
6647
6717
  ) }),
6648
6718
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuContent, { align: "end", children: [
6649
- user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyUser, children: [
6650
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, { className: "size-4" }),
6651
- "Copy ",
6652
- user.label.toLowerCase()
6653
- ] }) : null,
6654
- secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyPassword, children: [
6655
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, { className: "size-4" }),
6656
- "Copy password"
6657
- ] }) : null,
6658
- code ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyCode, children: [
6659
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ClockGlyph, { className: "size-4" }),
6660
- "Copy one-time code"
6661
- ] }) : null,
6662
- others.map((f) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => void run(f.label, () => port.reveal(item, f.key), f.handling !== "visible"), children: [
6663
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
6664
- "Copy ",
6665
- f.label.toLowerCase()
6666
- ] }, f.key)),
6667
- custom.map((c) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => void copy(c.value, c.label, { secret: false }), children: [
6668
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
6669
- "Copy ",
6670
- c.label.toLowerCase()
6671
- ] }, `custom:${c.key}`)),
6672
- user || secret || code || others.length > 0 || custom.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
6673
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => onSelect(item.id), children: [
6674
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRightGlyph, { className: "size-4" }),
6675
- "Open"
6676
- ] }),
6677
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyName, children: [
6678
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
6679
- "Copy name"
6680
- ] }),
6719
+ copyEntries.map(menuItem),
6720
+ copyEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
6721
+ entries.filter((e) => e.kind === "favorite" || e.kind === "open" || e.kind === "copy-name").map(menuItem),
6681
6722
  hostEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
6682
- hostEntries.map((h) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuItem, { onSelect: h.onSelect, children: h.label }, h.label))
6723
+ entries.filter((e) => e.kind === "host").map(menuItem)
6683
6724
  ] })
6684
6725
  ] })
6685
6726
  ] })
@@ -8187,15 +8228,17 @@ var import_controls34 = require("@ai-matrx/design-system/controls");
8187
8228
  var import_icons16 = require("@ai-matrx/design-system/icons");
8188
8229
  var import_jsx_runtime39 = require("react/jsx-runtime");
8189
8230
  var AUTHENTICATOR_LABEL = "Authenticator";
8190
- function vaultChoices(items, organizations, current) {
8231
+ function vaultChoices(items, organizations, _current) {
8191
8232
  const n = (f) => items.filter(f).length;
8192
8233
  const shared = n((i) => i.owner.kind === "shared");
8193
8234
  return [
8194
8235
  { value: "all", label: "All items", count: items.length },
8195
8236
  { value: "mine", label: "Personal", count: n((i) => i.owner.kind === "me") },
8196
8237
  ...shared > 0 ? [{ value: "shared", label: "Shared with me", count: shared }] : [],
8197
- // Only organizations that hold something; an empty one is a dead end in a vault list.
8198
- ...organizations.map((o) => ({ value: o.id, label: o.name, count: n((i) => i.owner.organizationId === o.id) })).filter((c) => c.count > 0 || c.value === current)
8238
+ // Every organization the person belongs to, empty ones too: an empty vault opens on its own
8239
+ // "Nothing here yet" with New item, so the first credential can go there. (The organization
8240
+ // list carries no role, so who may add is the server's answer to the save, said in place.)
8241
+ ...organizations.map((o) => ({ value: o.id, label: o.name, count: n((i) => i.owner.organizationId === o.id) }))
8199
8242
  ];
8200
8243
  }
8201
8244
  function VaultSelect({ value, choices, onChange }) {
@@ -8475,7 +8518,16 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8475
8518
  const load = (0, import_react42.useCallback)(() => {
8476
8519
  setError(null);
8477
8520
  setRefreshError(null);
8478
- port.load().then(apply).catch((e) => {
8521
+ let finished = false;
8522
+ port.load((partial) => {
8523
+ if (finished) return;
8524
+ setSnap(partial);
8525
+ setItems(partial.items);
8526
+ setDeleted(partial.deleted);
8527
+ }).then((s) => {
8528
+ finished = true;
8529
+ apply(s);
8530
+ }).catch((e) => {
8479
8531
  setSignedOut(e?.status === 401);
8480
8532
  setError(e instanceof Error ? e.message : "The vault could not be loaded.");
8481
8533
  });
@@ -8592,59 +8644,62 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8592
8644
  }
8593
8645
  const hasFavorites = port.lists.favorites !== void 0;
8594
8646
  const title = loc.vault === "all" && loc.view === "all" ? "All items" : viewLabel(loc.view, views);
8595
- const compactHeader = mode === "full" ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_auto] gap-2", children: [
8596
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(VaultSelect, { value: loc.vault, choices, onChange: (vault) => go({ vault, itemId: null, panel: null }) }),
8597
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_controls35.Select, { value: loc.view, onValueChange: (view) => go({ view, itemId: null, panel: null }), options: viewOptions(views, inThisVault.length, deleted.length, hasFavorites, port.lists.recents !== void 0, host.page !== void 0), "aria-label": "Show" }),
8598
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
8599
- import_controls35.Button,
8600
- {
8601
- variant: loc.view === "authenticator" ? "outline" : "quiet",
8602
- icon: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LockGlyph, {}),
8603
- "aria-label": AUTHENTICATOR_LABEL,
8604
- "aria-pressed": loc.view === "authenticator",
8605
- title: AUTHENTICATOR_LABEL,
8606
- onClick: () => go({ view: loc.view === "authenticator" ? "all" : "authenticator", itemId: null, mode: "view", panel: null }),
8607
- children: mode === "split" ? AUTHENTICATOR_LABEL : null
8608
- }
8609
- ),
8610
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenu, { children: [
8611
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_design_system26.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_controls35.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MoreGlyph, {}), "aria-label": "Vault actions" }) }),
8612
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuContent, { align: "end", children: [
8613
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "import" }), children: [
8614
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ImportGlyph, { className: "size-4" }),
8615
- "Import"
8616
- ] }),
8617
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "export" }), children: [
8618
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ExportGlyph, { className: "size-4" }),
8619
- "Export"
8620
- ] }),
8621
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "backup" }), children: [
8622
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SafeGlyph, { className: "size-4" }),
8623
- "Backup"
8624
- ] }),
8625
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "devices" }), children: [
8626
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ShieldGlyph, { className: "size-4" }),
8627
- "Browsers that fill"
8628
- ] }),
8629
- host.nativeImport ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "import-passkeys" }), children: [
8630
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ImportGlyph, { className: "size-4" }),
8631
- "Import passkeys"
8632
- ] }) : null,
8633
- host.nativeProvider && host.nativeProvider.status !== "unavailable" ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: host.nativeProvider.openSetup, children: [
8634
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ShieldGlyph, { className: "size-4" }),
8635
- "System AutoFill"
8636
- ] }) : null,
8637
- host.lock?.lock ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => void host.lock?.lock?.(), children: [
8638
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LockGlyph, { className: "size-4" }),
8639
- "Lock"
8640
- ] }) : null,
8641
- host.openWebVault ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: host.openWebVault, children: [
8642
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ExternalGlyph, { className: "size-4" }),
8643
- "Open on the web"
8644
- ] }) : null
8647
+ const compactHeader = mode === "full" ? void 0 : (
8648
+ // The two pickers keep a real width at every compact size (the list column is 19rem in split
8649
+ // mode): the Authenticator button is icon-only beside them, its name in its tooltip.
8650
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "grid grid-cols-[minmax(6rem,1fr)_minmax(6rem,1fr)_auto_auto] gap-2", "data-vault-compact-header": "", children: [
8651
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(VaultSelect, { value: loc.vault, choices, onChange: (vault) => go({ vault, itemId: null, panel: null }) }),
8652
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_controls35.Select, { value: loc.view, onValueChange: (view) => go({ view, itemId: null, panel: null }), options: viewOptions(views, inThisVault.length, deleted.length, hasFavorites, port.lists.recents !== void 0, host.page !== void 0), "aria-label": "Show" }),
8653
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
8654
+ import_controls35.Button,
8655
+ {
8656
+ variant: loc.view === "authenticator" ? "outline" : "quiet",
8657
+ icon: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LockGlyph, {}),
8658
+ "aria-label": AUTHENTICATOR_LABEL,
8659
+ "aria-pressed": loc.view === "authenticator",
8660
+ title: AUTHENTICATOR_LABEL,
8661
+ onClick: () => go({ view: loc.view === "authenticator" ? "all" : "authenticator", itemId: null, mode: "view", panel: null })
8662
+ }
8663
+ ),
8664
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenu, { children: [
8665
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_design_system26.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_controls35.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MoreGlyph, {}), "aria-label": "Vault actions" }) }),
8666
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuContent, { align: "end", children: [
8667
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "import" }), children: [
8668
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ImportGlyph, { className: "size-4" }),
8669
+ "Import"
8670
+ ] }),
8671
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "export" }), children: [
8672
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ExportGlyph, { className: "size-4" }),
8673
+ "Export"
8674
+ ] }),
8675
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "backup" }), children: [
8676
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SafeGlyph, { className: "size-4" }),
8677
+ "Backup"
8678
+ ] }),
8679
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "devices" }), children: [
8680
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ShieldGlyph, { className: "size-4" }),
8681
+ "Browsers that fill"
8682
+ ] }),
8683
+ host.nativeImport ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "import-passkeys" }), children: [
8684
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ImportGlyph, { className: "size-4" }),
8685
+ "Import passkeys"
8686
+ ] }) : null,
8687
+ host.nativeProvider && host.nativeProvider.status !== "unavailable" ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: host.nativeProvider.openSetup, children: [
8688
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ShieldGlyph, { className: "size-4" }),
8689
+ "System AutoFill"
8690
+ ] }) : null,
8691
+ host.lock?.lock ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => void host.lock?.lock?.(), children: [
8692
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LockGlyph, { className: "size-4" }),
8693
+ "Lock"
8694
+ ] }) : null,
8695
+ host.openWebVault ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: host.openWebVault, children: [
8696
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ExternalGlyph, { className: "size-4" }),
8697
+ "Open on the web"
8698
+ ] }) : null
8699
+ ] })
8645
8700
  ] })
8646
8701
  ] })
8647
- ] });
8702
+ );
8648
8703
  const viewHeader = /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex flex-col gap-2", children: [
8649
8704
  compactHeader,
8650
8705
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_controls35.SearchField, { value: loc.q, onChange: (e) => go({ q: e.target.value }, "replace"), placeholder: `Search ${viewLabel(loc.view, views)}`, "aria-label": `Search ${viewLabel(loc.view, views)}` })
@@ -8687,6 +8742,8 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8687
8742
  onNew: () => go({ itemId: null, mode: "new", kind: null, panel: null }),
8688
8743
  onRefresh: refresh,
8689
8744
  refreshing,
8745
+ ...snap.pending && snap.pending.length > 0 ? { stillLoading: snap.pending } : {},
8746
+ ...hasFavorites ? { favorites: favorites ?? /* @__PURE__ */ new Set(), onToggleFavorite: toggleFavorite } : {},
8690
8747
  rowContextMenu: host.rowContextMenu ?? "package",
8691
8748
  ...host.rowMenuItems ? { rowMenuItems: host.rowMenuItems } : {},
8692
8749
  ...matchingIds ? { matchingIds } : {},
@@ -9576,6 +9633,12 @@ function createApiVaultPort(api, options = {}) {
9576
9633
  if (id) return { kind: "organization", label: orgs.find((o) => o.id === id)?.name ?? "Organization", organizationId: id };
9577
9634
  return mine || userId ? { kind: "me", label: "Personal", organizationId: null } : { kind: "shared", label: "Shared with me", organizationId: null };
9578
9635
  };
9636
+ const ownerInOrgList = (w, via, meId) => {
9637
+ if (w.organization_id) return orgOf(w.organization_id, null, false);
9638
+ if (w.user_id && meId && w.user_id === meId) return { kind: "me", label: "Personal", organizationId: null };
9639
+ if (via) return { kind: "organization", label: via.name, organizationId: via.id, memberShared: { userId: w.user_id ?? null } };
9640
+ return { kind: "shared", label: "Shared with me", organizationId: null };
9641
+ };
9579
9642
  async function refetch(id, owner) {
9580
9643
  const w = await account9.getItem(api, { path: { item_id: id } });
9581
9644
  return itemOf(w, owner, defs, auth);
@@ -9603,38 +9666,100 @@ function createApiVaultPort(api, options = {}) {
9603
9666
  io: createIoPort(ctx),
9604
9667
  fields: createFieldsPort(ctx),
9605
9668
  lists: createListsPort(ctx, options.userState),
9606
- async load() {
9669
+ async load(onProgress) {
9607
9670
  const catalogP = retrying(() => apps2.getAppCatalogKind(api, { path: { app: "matrx", kind: "credential_definition" } }));
9608
9671
  const authP = retrying(() => apps2.listAuthenticators(api));
9609
9672
  const userP = retrying(() => account9.listItems(api, { query: { principal_type: "user" } }));
9610
9673
  const sharedP = retrying(() => account9.sharedWithMe(api));
9611
9674
  const quiet = (p) => void p.catch(() => void 0);
9612
9675
  [catalogP, authP, userP, sharedP].forEach(quiet);
9613
- const org = options.organizations ? null : await retrying(() => platform.listMyOrganizations(api));
9614
- orgs = [...new Map((options.organizations ?? (org?.organizations ?? []).filter((o) => !o.archived_at).map((o) => ({ id: o.id, name: o.name }))).map((o) => [o.id, o])).values()];
9615
- const orgListsP = orgs.map((o) => retrying(() => account9.listItems(api, { query: { principal_type: "organization", organization_id: o.id } }, { organizationId: o.id })));
9616
- orgListsP.forEach(quiet);
9617
- const [catalog, mine] = await Promise.all([catalogP, userP]);
9618
- const [authSettled, sharedSettled, ...orgSettled] = await Promise.allSettled([authP, sharedP, ...orgListsP]);
9676
+ const raw = {
9677
+ orgLists: [],
9678
+ orgsKnown: options.organizations !== void 0,
9679
+ meId: null
9680
+ };
9619
9681
  const unavailable = [];
9620
- const authList = authSettled.status === "fulfilled" ? authSettled.value : (unavailable.push("One-time codes"), { entries: [] });
9621
- const orgLists = orgSettled.map((r, i) => r.status === "fulfilled" ? r.value : (unavailable.push(orgs[i]?.name ?? "An organization"), { items: [], count: 0 }));
9622
- const shared = sharedSettled.status === "fulfilled" ? sharedSettled.value : (unavailable.push("Shared with me"), { items: [], count: 0 });
9623
- const lists = [mine, ...orgLists, shared];
9624
- defs = new Map(parseDefinitions(catalog.filter((e) => e.is_active).map((e) => ({ key: e.key, payload: e.payload, sort_order: e.sort_order }))).map((d) => [d.key, d]));
9625
- auth = new Map((authList.entries ?? []).map((a) => [a.credential_item_id, a]));
9626
- const seen = /* @__PURE__ */ new Map();
9627
- const deleted = /* @__PURE__ */ new Map();
9628
- lists.forEach((l, i) => {
9629
- const sharedList = i === lists.length - 1;
9630
- for (const w of l.items) {
9631
- if (seen.has(w.id) || deleted.has(w.id) || isHiddenItem(w)) continue;
9632
- const owner = sharedList ? orgOf(w.organization_id, null, false) : orgOf(w.organization_id, w.user_id, i === 0);
9633
- const item = itemOf(w, sharedList && !w.organization_id ? { kind: "shared", label: "Shared with me", organizationId: null } : owner, defs, auth);
9682
+ let done = false;
9683
+ const build = () => {
9684
+ const catalog2 = raw.catalog ?? [];
9685
+ defs = new Map(parseDefinitions(catalog2.filter((e) => e.is_active).map((e) => ({ key: e.key, payload: e.payload, sort_order: e.sort_order }))).map((d) => [d.key, d]));
9686
+ const mineItems = raw.mine?.items ?? [];
9687
+ const meId = raw.meId ?? mineItems.find((w) => w.user_id)?.user_id ?? null;
9688
+ const seen = /* @__PURE__ */ new Map();
9689
+ const deleted = /* @__PURE__ */ new Map();
9690
+ const take = (w, owner) => {
9691
+ if (seen.has(w.id) || deleted.has(w.id) || isHiddenItem(w)) return;
9692
+ const item = itemOf(w, owner, defs, auth);
9634
9693
  (isDeleted(w) ? deleted : seen).set(w.id, item);
9694
+ };
9695
+ for (const w of mineItems) take(w, orgOf(w.organization_id, null, true));
9696
+ raw.orgLists.forEach((l, i) => {
9697
+ const via = orgs[i];
9698
+ for (const w of l?.items ?? []) take(w, ownerInOrgList(w, via, meId));
9699
+ });
9700
+ for (const w of raw.shared?.items ?? []) take(w, w.organization_id ? orgOf(w.organization_id, null, false) : { kind: "shared", label: "Shared with me", organizationId: null });
9701
+ const pending = done ? [] : [...raw.orgsKnown ? orgs.filter((_, i) => raw.orgLists[i] === void 0).map((o) => o.name) : ["your organizations"], ...raw.shared ? [] : ["Shared with me"]];
9702
+ return {
9703
+ unavailable: [...unavailable],
9704
+ ...pending.length > 0 ? { pending } : {},
9705
+ items: [...seen.values()],
9706
+ deleted: [...deleted.values()],
9707
+ organizations: orgs,
9708
+ definitions: [...defs.values()].sort((a, b) => a.sortOrder - b.sortOrder || a.label.localeCompare(b.label))
9709
+ };
9710
+ };
9711
+ const progress = () => {
9712
+ if (!done && onProgress && raw.catalog && raw.mine) onProgress(build());
9713
+ };
9714
+ const orgReportP = options.organizations ? null : retrying(() => platform.listMyOrganizations(api));
9715
+ if (orgReportP) quiet(orgReportP);
9716
+ const orgListsReady = (async () => {
9717
+ const report = orgReportP ? await orgReportP : null;
9718
+ raw.meId = report?.user_id ?? null;
9719
+ orgs = [...new Map((options.organizations ?? (report?.organizations ?? []).filter((o) => !o.archived_at).map((o) => ({ id: o.id, name: o.name }))).map((o) => [o.id, o])).values()];
9720
+ raw.orgLists = orgs.map(() => void 0);
9721
+ raw.orgsKnown = true;
9722
+ progress();
9723
+ return orgs.map((o, i) => {
9724
+ const p = retrying(() => account9.listItems(api, { query: { principal_type: "organization", organization_id: o.id } }, { organizationId: o.id }));
9725
+ const arrived = p.then((l) => {
9726
+ raw.orgLists[i] = l;
9727
+ progress();
9728
+ return l;
9729
+ });
9730
+ quiet(arrived);
9731
+ return arrived;
9732
+ });
9733
+ })();
9734
+ quiet(orgListsReady);
9735
+ const authDone = authP.then((a) => {
9736
+ auth = new Map((a.entries ?? []).map((e) => [e.credential_item_id, e]));
9737
+ progress();
9738
+ }, () => {
9739
+ unavailable.push("One-time codes");
9740
+ });
9741
+ const sharedDone = sharedP.then((sh) => {
9742
+ raw.shared = sh;
9743
+ progress();
9744
+ }, () => {
9745
+ raw.shared = { items: [], count: 0 };
9746
+ unavailable.push("Shared with me");
9747
+ });
9748
+ const [catalog, mine] = await Promise.all([catalogP, userP]);
9749
+ raw.catalog = catalog;
9750
+ raw.mine = mine;
9751
+ progress();
9752
+ const orgPs = await orgListsReady;
9753
+ const orgSettled = await Promise.allSettled(orgPs);
9754
+ orgSettled.forEach((r, i) => {
9755
+ if (r.status === "rejected") {
9756
+ raw.orgLists[i] = { items: [], count: 0 };
9757
+ unavailable.push(orgs[i]?.name ?? "An organization");
9635
9758
  }
9636
9759
  });
9637
- return { unavailable, items: [...seen.values()], deleted: [...deleted.values()], organizations: orgs, definitions: [...defs.values()].sort((a, b) => a.sortOrder - b.sortOrder || a.label.localeCompare(b.label)) };
9760
+ await Promise.all([authDone, sharedDone]);
9761
+ done = true;
9762
+ return build();
9638
9763
  },
9639
9764
  async reveal(item, fieldKey2) {
9640
9765
  const field = item.fields.find((f) => f.key === fieldKey2);