@ai-matrx/vault 0.1.37 → 0.1.39

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,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.39
4
+
5
+ - **Organization vaults never read as empty by mistake** (2026-10-23): a 404 from `GET /vault/organization-items` still falls back to one read per organization, but now says so once per port (new `ApiVaultPortOptions.onHostProblem(message, detail)`; without it, `console.warn`); an organization the server refused, names unavailable or leaves out is listed under `unavailable`, never shown as an empty vault; the camera note in Add authenticator is one line of 60 characters or fewer ("No camera. Paste the key or choose a QR image."). Proof: `src/organization-items-load.test.ts`, `src/first-code-closes.test.tsx`, `src/review-0-1-36.test.tsx`, `src/review-0-1-37.test.tsx`. Consumer action: needs `@ai-matrx/design-system` with the Select hint that never truncates (the release train publishes both); a host may pass `onHostProblem` to forward it to its error capture; nothing else.
6
+
7
+ ## 0.1.38
8
+
9
+ - **Website vault walk fixes** (2026-10-23): a row with an authenticator copies its one-time code from the hover quick actions ("One-time code copied"); "Add authenticator" shows the setup-key box first and starts the camera by itself only when the device has one, with the preview and any camera failure UNDER the box as a small note (no window over it; `hasCameraDevice()` in `authenticator/qr.ts`, `QrScanInline` replaces `QrScanPanel`); the first-code window closes when the person moves to another view, item, edit or panel (`useShownUntilMove`); the Vault switcher, Move to and Copy to list organizations recently used and non-empty first, then by name (`orderOrganizations`), tell same-named ones apart by short id (`organizationHints`) and show a search from `vault.organization_search_at` (new knob, default 8, read through `host.knobs`; `host.organizationSearchAt` overrides); after "Copy made in …" the panel offers Open copy (`ItemPanelProps.onOpenItem`); the load reads every organization vault in ONE request (`GET /vault/organization-items`) instead of one per organization, own items still first. Proof: `src/review-0-1-37.test.tsx`. Consumer action: needs `@ai-matrx/api` with `account.organizationItems` and `@ai-matrx/design-system` with Select `hint`/`searchAt` (the release train publishes all three); nothing else.
10
+
3
11
  ## 0.1.37
4
12
 
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`.
13
+ - **Website vault walk fixes, items 7-11** (2026-10-23): a row with an authenticator copies its one-time code from the hover quick actions; "Add authenticator" shows the setup-key box first and starts the camera only when the device has one (`hasCameraDevice()`); the first-code window closes when the person moves to another view, item, edit or panel (`useShownUntilMove`); the Vault switcher, Move to and Copy to list recently used and non-empty organizations first, then by name, with short ids for same-named ones and a search from `vault.organization_search_at` (`orderOrganizations`, `organizationHints`, `ORGANIZATION_SEARCH_AT_KNOB`); after "Copy made in ..." the panel offers Open copy (`ItemPanelProps.onOpenItem`). Proof: `src/review-0-1-37.test.tsx`. Consumer action: none.
7
14
 
8
15
  ## 0.1.36
9
16
 
package/dist/index.cjs CHANGED
@@ -1928,6 +1928,11 @@ function useCanScan() {
1928
1928
  }, []);
1929
1929
  return ok;
1930
1930
  }
1931
+ var CAMERA_NOTES = {
1932
+ blocked: "Camera blocked. Paste the key or choose a QR image.",
1933
+ missing: "No camera. Paste the key or choose a QR image.",
1934
+ failed: "Camera did not start. Paste the key or choose a QR image."
1935
+ };
1931
1936
  function QrScanInline({ onResult, onClose }) {
1932
1937
  const video = (0, import_react10.useRef)(null);
1933
1938
  const [error, setError] = (0, import_react10.useState)(null);
@@ -1973,7 +1978,7 @@ function QrScanInline({ onResult, onClose }) {
1973
1978
  }).catch((e) => {
1974
1979
  if (!live) return;
1975
1980
  const name = e?.name;
1976
- setError(name === "NotAllowedError" ? "Camera access was not allowed." : name === "NotFoundError" ? "No camera found." : "The camera could not start.");
1981
+ setError(name === "NotAllowedError" ? CAMERA_NOTES.blocked : name === "NotFoundError" ? CAMERA_NOTES.missing : CAMERA_NOTES.failed);
1977
1982
  });
1978
1983
  return () => {
1979
1984
  live = false;
@@ -1983,10 +1988,7 @@ function QrScanInline({ onResult, onClose }) {
1983
1988
  if (error) {
1984
1989
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "flex items-center gap-2 px-1 pt-1 text-xs text-muted-foreground", role: "status", "data-camera-note": "", children: [
1985
1990
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CameraGlyph, { className: "size-3.5 shrink-0", "aria-hidden": true }),
1986
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "min-w-0 flex-1 truncate", children: [
1987
- error,
1988
- " Paste the setup key or choose a QR image."
1989
- ] }),
1991
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "min-w-0 flex-1 truncate", children: error }),
1990
1992
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_controls6.Button, { variant: "quiet", onClick: onClose, children: "Dismiss" })
1991
1993
  ] });
1992
1994
  }
@@ -10025,6 +10027,13 @@ async function retrying(run, tries = 3) {
10025
10027
  }
10026
10028
  function createApiVaultPort(hostApi, options = {}) {
10027
10029
  const api = withServerReasons(hostApi);
10030
+ let problemTold = false;
10031
+ const hostProblem = (message2, detail) => {
10032
+ if (problemTold) return;
10033
+ problemTold = true;
10034
+ if (options.onHostProblem) options.onHostProblem(message2, detail);
10035
+ else console.warn(`[vault] ${message2}`, detail);
10036
+ };
10028
10037
  let defs = /* @__PURE__ */ new Map();
10029
10038
  let auth = /* @__PURE__ */ new Map();
10030
10039
  let orgs = [];
@@ -10072,6 +10081,8 @@ function createApiVaultPort(hostApi, options = {}) {
10072
10081
  const sharedP = retrying(() => account9.sharedWithMe(api));
10073
10082
  const quiet = (p) => void p.catch(() => void 0);
10074
10083
  [catalogP, authP, userP, sharedP].forEach(quiet);
10084
+ const orgItemsP = retrying(() => account9.organizationItems(api));
10085
+ quiet(orgItemsP);
10075
10086
  const raw = {
10076
10087
  orgLists: [],
10077
10088
  orgsKnown: options.organizations !== void 0,
@@ -10119,6 +10130,29 @@ function createApiVaultPort(hostApi, options = {}) {
10119
10130
  raw.orgLists = orgs.map(() => void 0);
10120
10131
  raw.orgsKnown = true;
10121
10132
  progress();
10133
+ const one = await orgItemsP.then(
10134
+ (r) => r,
10135
+ (e) => {
10136
+ if (e?.status === 404) {
10137
+ hostProblem("GET /vault/organization-items answered 404; reading each organization vault separately", e);
10138
+ return null;
10139
+ }
10140
+ return "failed";
10141
+ }
10142
+ );
10143
+ if (one) {
10144
+ const byOrg = one === "failed" ? /* @__PURE__ */ new Map() : new Map(one.views.map((v) => [v.organization_id, v.items]));
10145
+ const failed = one === "failed" ? new Set(orgs.map((o) => o.id)) : new Set(one.unavailable);
10146
+ const settled = orgs.map((o, i) => {
10147
+ const items = byOrg.get(o.id);
10148
+ if (failed.has(o.id) || items === void 0) return Promise.reject(new Error(`${o.name} is unavailable`));
10149
+ raw.orgLists[i] = { items, count: items.length };
10150
+ return Promise.resolve(raw.orgLists[i]);
10151
+ });
10152
+ settled.forEach(quiet);
10153
+ progress();
10154
+ return settled;
10155
+ }
10122
10156
  return orgs.map((o, i) => {
10123
10157
  const p = retrying(() => account9.listItems(api, { query: { principal_type: "organization", organization_id: o.id } }, { organizationId: o.id }));
10124
10158
  const arrived = p.then((l) => {