@ai-matrx/vault 0.1.20 → 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,5 +1,23 @@
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
+
12
+ ## 0.1.21
13
+
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`
20
+
3
21
  ## 0.1.20
4
22
 
5
23
  - **A multi-line secret edits as several lines.** A field whose type says multi-line, any field under a notes key (an old **Secure note**, `secure_notes`) and any value that already holds a line break now edit in a multi-line box — a one-line box dropped the line breaks on save. Shown values keep their lines too. Guard: `src/vault-gaps-2026-10-19.test.tsx`. Consumer action: none.
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,
@@ -622,6 +623,8 @@ function ConfirmIdentityDialog({ open, identity, onDone, onCancel }) {
622
623
  var import_react5 = require("react");
623
624
  var import_controls2 = require("@ai-matrx/design-system/controls");
624
625
  var import_design_system5 = require("@ai-matrx/design-system");
626
+ var import_outbound = require("@ai-matrx/kit/outbound");
627
+ var import_outbound_react = require("@ai-matrx/kit/outbound-react");
625
628
  var import_icons2 = require("@ai-matrx/design-system/icons");
626
629
 
627
630
  // src/hooks.ts
@@ -833,13 +836,13 @@ function PlainRow({ label, value, hint: hint2, openUrl, extra, multiline = false
833
836
  top: multiline,
834
837
  actions: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
835
838
  extra,
836
- isUrl && openUrl ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExternalGlyph, {}), "aria-label": `Open ${value}`, onClick: () => openUrl(value) }) : null,
839
+ isUrl && openUrl ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExternalGlyph, {}), "aria-label": `Open ${value}`, onClick: () => openUrl((0, import_outbound.withReferral)(value)) }) : null,
837
840
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_icons2.CopyIcon, {}), "aria-label": `Copy ${typeof label === "string" ? label : "value"}`, onClick: () => void copyValue2(value, typeof label === "string" && label ? label : "Value", { secret: false }) })
838
841
  ] }),
839
842
  children: isUrl && openUrl ? (
840
843
  // A real link (right-click, copy link address, open in a new tab); a click goes through the host's opener.
841
844
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
842
- "a",
845
+ import_outbound_react.OutboundLink,
843
846
  {
844
847
  href: value,
845
848
  target: "_blank",
@@ -847,7 +850,7 @@ function PlainRow({ label, value, hint: hint2, openUrl, extra, multiline = false
847
850
  className: "block max-w-full truncate text-primary hover:underline",
848
851
  onClick: (e) => {
849
852
  e.preventDefault();
850
- openUrl(value);
853
+ openUrl((0, import_outbound.withReferral)(value));
851
854
  },
852
855
  children: value.replace(/^https?:\/\//i, "").replace(/\/$/, "")
853
856
  }
@@ -916,6 +919,19 @@ var import_format3 = require("@ai-matrx/kit/format");
916
919
  var import_format = require("@ai-matrx/kit/format");
917
920
 
918
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
+ }
919
935
  var DEFAULT_LOCATION = { vault: "all", view: "all", q: "", sort: "name", itemId: null, mode: "view", kind: null, url: "", panel: null };
920
936
  var SORTS = ["name", "name-desc", "website", "updated", "created", "viewed"];
921
937
  function locationFromParams(params, itemId = params.get("item")) {
@@ -6055,6 +6071,7 @@ function ItemPane(props) {
6055
6071
  const setValue = (key, v) => setDraft((d) => ({ ...d, values: { ...d.values, [key]: v } }));
6056
6072
  const canEdit = item ? item.capabilities.canEdit || item.capabilities.canManage : true;
6057
6073
  const canManage = item?.capabilities.canManage ?? false;
6074
+ const manageReason = item ? manageUnavailableReason(item) : null;
6058
6075
  async function save() {
6059
6076
  if (!definition && !item) return;
6060
6077
  setBusy(true);
@@ -6201,7 +6218,8 @@ function ItemPane(props) {
6201
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 }),
6202
6219
  /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "flex min-w-0 items-center gap-1 text-xs text-muted-foreground", children: [
6203
6220
  typeLabel ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "truncate", children: typeLabel }) : null,
6204
- 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,
6205
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
6206
6224
  ] })
6207
6225
  ] }),
@@ -6239,9 +6257,9 @@ function ItemPane(props) {
6239
6257
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ShareGlyph, { className: "size-4" }),
6240
6258
  "Share"
6241
6259
  ] }) : null,
6242
- 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: [
6243
6261
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MoveGlyph, { className: "size-4" }),
6244
- "Move or copy"
6262
+ canManage ? "Move or copy" : "Copy to\u2026"
6245
6263
  ] }) : null,
6246
6264
  item.capabilities.canReveal ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_design_system17.DropdownMenuItem, { onSelect: () => onPanel("send"), children: [
6247
6265
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SendGlyph, { className: "size-4" }),
@@ -6265,6 +6283,17 @@ function ItemPane(props) {
6265
6283
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(TrashGlyph, { className: "size-4" }),
6266
6284
  "Delete"
6267
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 })
6268
6297
  ] }) : null
6269
6298
  ] })
6270
6299
  ] })
@@ -6495,6 +6524,17 @@ function ItemPane(props) {
6495
6524
  ] }) });
6496
6525
  }
6497
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
+
6498
6538
  // src/ItemList.tsx
6499
6539
  var import_jsx_runtime28 = require("react/jsx-runtime");
6500
6540
  var RECOVERY_KEYS = /^recovery_codes?$/;
@@ -6516,7 +6556,7 @@ function subtitleOf(item, withOwner) {
6516
6556
  const what = loginSiteOf(item) || item.definitionLabel;
6517
6557
  return withOwner ? `${what} \xB7 ${item.owner.label}` : what;
6518
6558
  }
6519
- 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 }) {
6520
6560
  const sorts = SORTS2.filter((s) => canSortViewed || s.value !== "viewed");
6521
6561
  const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
6522
6562
  const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
@@ -6537,7 +6577,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6537
6577
  }
6538
6578
  };
6539
6579
  const mixedOwners = showOwner ?? new Set(items.map((i) => i.owner.organizationId ?? i.owner.kind)).size > 1;
6540
- 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);
6541
6581
  const sortLabel = sorts.find((s) => s.value === sort)?.label ?? "Name A\u2013Z";
6542
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: [
6543
6583
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children: [
@@ -6547,10 +6587,18 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6547
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 })
6548
6588
  ] }),
6549
6589
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center justify-between gap-2 px-1", children: [
6550
- /* @__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: [
6551
6591
  items.length,
6552
6592
  " ",
6553
- 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
6554
6602
  ] }),
6555
6603
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "flex shrink-0 items-center gap-0.5", children: [
6556
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,
@@ -6562,7 +6610,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6562
6610
  ] })
6563
6611
  ] }),
6564
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: [
6565
- /* @__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." }),
6566
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" })
6567
6615
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("ul", { ref: listRef, className: "flex flex-col gap-px", children: [
6568
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,
@@ -6572,7 +6620,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
6572
6620
  ] }) })
6573
6621
  ] });
6574
6622
  }
6575
- function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra }) {
6623
+ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu, extra, favorite }) {
6576
6624
  const confirmed = useConfirmed();
6577
6625
  const copy = useCopy();
6578
6626
  const notify = useNotify();
@@ -6600,9 +6648,32 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6600
6648
  const others = otherCopyFields(item, user, secret);
6601
6649
  const custom = canUse ? item.custom.filter((c) => c.value) : [];
6602
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);
6603
6673
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
6604
6674
  "li",
6605
6675
  {
6676
+ ref: (el) => registerRowMenu(el, () => entriesRef.current),
6606
6677
  className: "group relative",
6607
6678
  "data-item": item.id,
6608
6679
  onContextMenu: ownContextMenu ? (e) => {
@@ -6628,7 +6699,8 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6628
6699
  }
6629
6700
  ),
6630
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: [
6631
- 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,
6632
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,
6633
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
6634
6706
  ] }) : null,
@@ -6644,40 +6716,11 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
6644
6716
  }
6645
6717
  ) }),
6646
6718
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuContent, { align: "end", children: [
6647
- user ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyUser, children: [
6648
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(UserGlyph, { className: "size-4" }),
6649
- "Copy ",
6650
- user.label.toLowerCase()
6651
- ] }) : null,
6652
- secret ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyPassword, children: [
6653
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(KeyGlyph, { className: "size-4" }),
6654
- "Copy password"
6655
- ] }) : null,
6656
- code ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyCode, children: [
6657
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ClockGlyph, { className: "size-4" }),
6658
- "Copy one-time code"
6659
- ] }) : null,
6660
- 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: [
6661
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
6662
- "Copy ",
6663
- f.label.toLowerCase()
6664
- ] }, f.key)),
6665
- custom.map((c) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => void copy(c.value, c.label, { secret: false }), children: [
6666
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
6667
- "Copy ",
6668
- c.label.toLowerCase()
6669
- ] }, `custom:${c.key}`)),
6670
- user || secret || code || others.length > 0 || custom.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
6671
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: () => onSelect(item.id), children: [
6672
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ChevronRightGlyph, { className: "size-4" }),
6673
- "Open"
6674
- ] }),
6675
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_design_system19.DropdownMenuItem, { onSelect: copyName, children: [
6676
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_icons12.CopyIcon, { className: "size-4" }),
6677
- "Copy name"
6678
- ] }),
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),
6679
6722
  hostEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_design_system19.DropdownMenuSeparator, {}) : null,
6680
- 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)
6681
6724
  ] })
6682
6725
  ] })
6683
6726
  ] })
@@ -8185,15 +8228,17 @@ var import_controls34 = require("@ai-matrx/design-system/controls");
8185
8228
  var import_icons16 = require("@ai-matrx/design-system/icons");
8186
8229
  var import_jsx_runtime39 = require("react/jsx-runtime");
8187
8230
  var AUTHENTICATOR_LABEL = "Authenticator";
8188
- function vaultChoices(items, organizations, current) {
8231
+ function vaultChoices(items, organizations, _current) {
8189
8232
  const n = (f) => items.filter(f).length;
8190
8233
  const shared = n((i) => i.owner.kind === "shared");
8191
8234
  return [
8192
8235
  { value: "all", label: "All items", count: items.length },
8193
8236
  { value: "mine", label: "Personal", count: n((i) => i.owner.kind === "me") },
8194
8237
  ...shared > 0 ? [{ value: "shared", label: "Shared with me", count: shared }] : [],
8195
- // Only organizations that hold something; an empty one is a dead end in a vault list.
8196
- ...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) }))
8197
8242
  ];
8198
8243
  }
8199
8244
  function VaultSelect({ value, choices, onChange }) {
@@ -8473,7 +8518,16 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8473
8518
  const load = (0, import_react42.useCallback)(() => {
8474
8519
  setError(null);
8475
8520
  setRefreshError(null);
8476
- 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) => {
8477
8531
  setSignedOut(e?.status === 401);
8478
8532
  setError(e instanceof Error ? e.message : "The vault could not be loaded.");
8479
8533
  });
@@ -8590,59 +8644,62 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8590
8644
  }
8591
8645
  const hasFavorites = port.lists.favorites !== void 0;
8592
8646
  const title = loc.vault === "all" && loc.view === "all" ? "All items" : viewLabel(loc.view, views);
8593
- 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: [
8594
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(VaultSelect, { value: loc.vault, choices, onChange: (vault) => go({ vault, itemId: null, panel: null }) }),
8595
- /* @__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" }),
8596
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
8597
- import_controls35.Button,
8598
- {
8599
- variant: loc.view === "authenticator" ? "outline" : "quiet",
8600
- icon: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LockGlyph, {}),
8601
- "aria-label": AUTHENTICATOR_LABEL,
8602
- "aria-pressed": loc.view === "authenticator",
8603
- title: AUTHENTICATOR_LABEL,
8604
- onClick: () => go({ view: loc.view === "authenticator" ? "all" : "authenticator", itemId: null, mode: "view", panel: null }),
8605
- children: mode === "split" ? AUTHENTICATOR_LABEL : null
8606
- }
8607
- ),
8608
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenu, { children: [
8609
- /* @__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" }) }),
8610
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuContent, { align: "end", children: [
8611
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "import" }), children: [
8612
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ImportGlyph, { className: "size-4" }),
8613
- "Import"
8614
- ] }),
8615
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "export" }), children: [
8616
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ExportGlyph, { className: "size-4" }),
8617
- "Export"
8618
- ] }),
8619
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "backup" }), children: [
8620
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SafeGlyph, { className: "size-4" }),
8621
- "Backup"
8622
- ] }),
8623
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "devices" }), children: [
8624
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ShieldGlyph, { className: "size-4" }),
8625
- "Browsers that fill"
8626
- ] }),
8627
- host.nativeImport ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => go({ panel: "import-passkeys" }), children: [
8628
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ImportGlyph, { className: "size-4" }),
8629
- "Import passkeys"
8630
- ] }) : null,
8631
- host.nativeProvider && host.nativeProvider.status !== "unavailable" ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: host.nativeProvider.openSetup, children: [
8632
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ShieldGlyph, { className: "size-4" }),
8633
- "System AutoFill"
8634
- ] }) : null,
8635
- host.lock?.lock ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: () => void host.lock?.lock?.(), children: [
8636
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(LockGlyph, { className: "size-4" }),
8637
- "Lock"
8638
- ] }) : null,
8639
- host.openWebVault ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_design_system26.DropdownMenuItem, { onSelect: host.openWebVault, children: [
8640
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ExternalGlyph, { className: "size-4" }),
8641
- "Open on the web"
8642
- ] }) : 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
+ ] })
8643
8700
  ] })
8644
8701
  ] })
8645
- ] });
8702
+ );
8646
8703
  const viewHeader = /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex flex-col gap-2", children: [
8647
8704
  compactHeader,
8648
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)}` })
@@ -8685,6 +8742,8 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
8685
8742
  onNew: () => go({ itemId: null, mode: "new", kind: null, panel: null }),
8686
8743
  onRefresh: refresh,
8687
8744
  refreshing,
8745
+ ...snap.pending && snap.pending.length > 0 ? { stillLoading: snap.pending } : {},
8746
+ ...hasFavorites ? { favorites: favorites ?? /* @__PURE__ */ new Set(), onToggleFavorite: toggleFavorite } : {},
8688
8747
  rowContextMenu: host.rowContextMenu ?? "package",
8689
8748
  ...host.rowMenuItems ? { rowMenuItems: host.rowMenuItems } : {},
8690
8749
  ...matchingIds ? { matchingIds } : {},
@@ -9574,6 +9633,12 @@ function createApiVaultPort(api, options = {}) {
9574
9633
  if (id) return { kind: "organization", label: orgs.find((o) => o.id === id)?.name ?? "Organization", organizationId: id };
9575
9634
  return mine || userId ? { kind: "me", label: "Personal", organizationId: null } : { kind: "shared", label: "Shared with me", organizationId: null };
9576
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
+ };
9577
9642
  async function refetch(id, owner) {
9578
9643
  const w = await account9.getItem(api, { path: { item_id: id } });
9579
9644
  return itemOf(w, owner, defs, auth);
@@ -9601,38 +9666,100 @@ function createApiVaultPort(api, options = {}) {
9601
9666
  io: createIoPort(ctx),
9602
9667
  fields: createFieldsPort(ctx),
9603
9668
  lists: createListsPort(ctx, options.userState),
9604
- async load() {
9669
+ async load(onProgress) {
9605
9670
  const catalogP = retrying(() => apps2.getAppCatalogKind(api, { path: { app: "matrx", kind: "credential_definition" } }));
9606
9671
  const authP = retrying(() => apps2.listAuthenticators(api));
9607
9672
  const userP = retrying(() => account9.listItems(api, { query: { principal_type: "user" } }));
9608
9673
  const sharedP = retrying(() => account9.sharedWithMe(api));
9609
9674
  const quiet = (p) => void p.catch(() => void 0);
9610
9675
  [catalogP, authP, userP, sharedP].forEach(quiet);
9611
- const org = options.organizations ? null : await retrying(() => platform.listMyOrganizations(api));
9612
- 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()];
9613
- const orgListsP = orgs.map((o) => retrying(() => account9.listItems(api, { query: { principal_type: "organization", organization_id: o.id } }, { organizationId: o.id })));
9614
- orgListsP.forEach(quiet);
9615
- const [catalog, mine] = await Promise.all([catalogP, userP]);
9616
- 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
+ };
9617
9681
  const unavailable = [];
9618
- const authList = authSettled.status === "fulfilled" ? authSettled.value : (unavailable.push("One-time codes"), { entries: [] });
9619
- const orgLists = orgSettled.map((r, i) => r.status === "fulfilled" ? r.value : (unavailable.push(orgs[i]?.name ?? "An organization"), { items: [], count: 0 }));
9620
- const shared = sharedSettled.status === "fulfilled" ? sharedSettled.value : (unavailable.push("Shared with me"), { items: [], count: 0 });
9621
- const lists = [mine, ...orgLists, shared];
9622
- 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]));
9623
- auth = new Map((authList.entries ?? []).map((a) => [a.credential_item_id, a]));
9624
- const seen = /* @__PURE__ */ new Map();
9625
- const deleted = /* @__PURE__ */ new Map();
9626
- lists.forEach((l, i) => {
9627
- const sharedList = i === lists.length - 1;
9628
- for (const w of l.items) {
9629
- if (seen.has(w.id) || deleted.has(w.id) || isHiddenItem(w)) continue;
9630
- const owner = sharedList ? orgOf(w.organization_id, null, false) : orgOf(w.organization_id, w.user_id, i === 0);
9631
- 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);
9632
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");
9633
9758
  }
9634
9759
  });
9635
- 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();
9636
9763
  },
9637
9764
  async reveal(item, fieldKey2) {
9638
9765
  const field = item.fields.find((f) => f.key === fieldKey2);