@ai-matrx/vault 0.1.55 → 0.1.57
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 +10 -1
- package/dist/index.cjs +274 -165
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +324 -215
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -432,7 +432,7 @@ function PasswordHealthView(props) {
|
|
|
432
432
|
}
|
|
433
433
|
|
|
434
434
|
// src/VaultApp.tsx
|
|
435
|
-
import { useCallback as useCallback4, useEffect as useEffect23, useMemo as useMemo6, useRef as
|
|
435
|
+
import { useCallback as useCallback4, useEffect as useEffect23, useMemo as useMemo6, useRef as useRef21, useState as useState41, useSyncExternalStore as useSyncExternalStore5 } from "react";
|
|
436
436
|
import { DropdownMenu as DropdownMenu5, DropdownMenuContent as DropdownMenuContent5, DropdownMenuItem as DropdownMenuItem5, DropdownMenuTrigger as DropdownMenuTrigger5, LookScope } from "@ai-matrx/design-system";
|
|
437
437
|
import { Button as Button34, SearchField as SearchField4, Select as Select12 } from "@ai-matrx/design-system/controls";
|
|
438
438
|
import { confirm as confirm14 } from "@ai-matrx/kit/confirm-opener";
|
|
@@ -1440,7 +1440,7 @@ function ItemIcon({ definitionKey, family, site, size = "md", inverted = false }
|
|
|
1440
1440
|
}
|
|
1441
1441
|
|
|
1442
1442
|
// src/authenticator/AddOtpPanel.tsx
|
|
1443
|
-
import { useState as useState11 } from "react";
|
|
1443
|
+
import { useRef as useRef9, useState as useState11 } from "react";
|
|
1444
1444
|
import { Button as Button8, Field as Field3, Select as Select2 } from "@ai-matrx/design-system/controls";
|
|
1445
1445
|
|
|
1446
1446
|
// src/authenticator/CodeParts.tsx
|
|
@@ -1968,7 +1968,7 @@ function OtpSetupRow({ value, onChange, label = "Authenticator", placeholder = S
|
|
|
1968
1968
|
}
|
|
1969
1969
|
|
|
1970
1970
|
// src/authenticator/TotpRows.tsx
|
|
1971
|
-
import { useState as useState10 } from "react";
|
|
1971
|
+
import { useRef as useRef8, useState as useState10 } from "react";
|
|
1972
1972
|
import { Button as Button7, Select } from "@ai-matrx/design-system/controls";
|
|
1973
1973
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@ai-matrx/design-system";
|
|
1974
1974
|
import { confirm as confirm2 } from "@ai-matrx/kit/confirm-opener";
|
|
@@ -2085,10 +2085,22 @@ function TotpRow({ port, item, onChanged }) {
|
|
|
2085
2085
|
function TotpEditRow({ value, onChange, hasExisting, item, port, onEnrolled, onRecognized }) {
|
|
2086
2086
|
const confirmed = useConfirmed();
|
|
2087
2087
|
const [status, setStatus] = useState10(null);
|
|
2088
|
+
const [retryImage, setRetryImage] = useState10(null);
|
|
2089
|
+
const latestItem = useRef8(item);
|
|
2090
|
+
latestItem.current = item;
|
|
2088
2091
|
const uploadImage = item && port ? async (file) => {
|
|
2092
|
+
setRetryImage(file);
|
|
2089
2093
|
setStatus("Adding from image\u2026");
|
|
2090
2094
|
try {
|
|
2091
|
-
const entry = await confirmed(() =>
|
|
2095
|
+
const entry = await confirmed(async () => {
|
|
2096
|
+
const current = await port.get(item);
|
|
2097
|
+
const visible = latestItem.current;
|
|
2098
|
+
if (!visible || visible.id !== item.id || visible.authenticatorState !== "known" || current.authenticatorState !== "known" || !item.authenticator && (visible.authenticator || current.authenticator)) {
|
|
2099
|
+
throw new Error("Authenticator status unavailable. Retry after refreshing.");
|
|
2100
|
+
}
|
|
2101
|
+
return port.authenticator.enrolQrImage(current, file, file.name);
|
|
2102
|
+
});
|
|
2103
|
+
setRetryImage(null);
|
|
2092
2104
|
setStatus(entry.issuer ? `Added \xB7 ${entry.issuer}` : "Added");
|
|
2093
2105
|
onChange("");
|
|
2094
2106
|
onEnrolled?.({ ...item, authenticator: { digits: entry.digits, period: entry.period, enabled: entry.enabled } });
|
|
@@ -2105,7 +2117,11 @@ function TotpEditRow({ value, onChange, hasExisting, item, port, onEnrolled, onR
|
|
|
2105
2117
|
onChange(v);
|
|
2106
2118
|
},
|
|
2107
2119
|
placeholder: hasExisting ? "Paste a new key to replace" : SETUP_KEY_PLACEHOLDER,
|
|
2108
|
-
status,
|
|
2120
|
+
status: status && retryImage && uploadImage ? /* @__PURE__ */ jsxs8(Fragment6, { children: [
|
|
2121
|
+
status,
|
|
2122
|
+
" ",
|
|
2123
|
+
/* @__PURE__ */ jsx9(Button7, { variant: "quiet", onClick: () => void uploadImage(retryImage), children: "Retry" })
|
|
2124
|
+
] }) : status,
|
|
2109
2125
|
...uploadImage ? { onImage: (f) => void uploadImage(f) } : {},
|
|
2110
2126
|
...onRecognized ? { onRecognized } : {}
|
|
2111
2127
|
}
|
|
@@ -2166,7 +2182,9 @@ function normalizeWebsite(raw) {
|
|
|
2166
2182
|
}
|
|
2167
2183
|
function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChanged, onAdded, onDone }) {
|
|
2168
2184
|
const confirmed = useConfirmed();
|
|
2169
|
-
const
|
|
2185
|
+
const itemsRef = useRef9(items);
|
|
2186
|
+
itemsRef.current = items;
|
|
2187
|
+
const candidates = items.filter((i) => i.authenticatorState === "known" && !i.authenticator && !i.deletion && (i.capabilities.canEdit || i.capabilities.canManage)).sort((a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base" }));
|
|
2170
2188
|
const [input, setInput] = useState11("");
|
|
2171
2189
|
const [image, setImage] = useState11(null);
|
|
2172
2190
|
const [target, setTarget] = useState11(itemId && candidates.some((c) => c.id === itemId) ? itemId : NEW);
|
|
@@ -2194,18 +2212,26 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
|
|
|
2194
2212
|
setBusy(true);
|
|
2195
2213
|
setError(null);
|
|
2196
2214
|
try {
|
|
2197
|
-
let item = isNew ? created :
|
|
2215
|
+
let item = isNew ? created : itemsRef.current.find((i) => i.id === target) ?? null;
|
|
2216
|
+
let freshlyCreated = false;
|
|
2217
|
+
if (!isNew && (!item || item.authenticatorState !== "known" || item.authenticator)) throw new Error("Authenticator status unavailable. Retry after refreshing.");
|
|
2198
2218
|
if (!item) {
|
|
2199
2219
|
const def = LOGIN_KEYS.map((k) => definitions.find((d) => d.key === k)).find((d) => Boolean(d));
|
|
2200
2220
|
if (!def) throw new Error("No login type is available.");
|
|
2201
2221
|
const r = await port.create(def, null, { ...draftOf(null), name: name.trim(), loginUrls: websiteUrl ? [websiteUrl] : [] });
|
|
2202
2222
|
item = r.item;
|
|
2223
|
+
freshlyCreated = true;
|
|
2203
2224
|
setCreated(item);
|
|
2204
2225
|
onChanged();
|
|
2205
2226
|
}
|
|
2206
2227
|
const it = item;
|
|
2207
|
-
const entry = await confirmed(() =>
|
|
2208
|
-
|
|
2228
|
+
const entry = await confirmed(async () => {
|
|
2229
|
+
const latest = freshlyCreated ? it : await port.get(it);
|
|
2230
|
+
const listed = itemsRef.current.find((i) => i.id === it.id);
|
|
2231
|
+
if (!freshlyCreated && (latest.authenticatorState !== "known" || latest.authenticator || !isNew && !listed) || listed && (listed.authenticatorState !== "known" || listed.authenticator)) throw new Error("Authenticator status unavailable. Retry after refreshing.");
|
|
2232
|
+
return image ? port.authenticator.enrolQrImage(latest, image, image.name) : port.authenticator.enrol(latest, input);
|
|
2233
|
+
});
|
|
2234
|
+
let next = { ...it, authenticatorState: "known", authenticator: { digits: entry.digits, period: entry.period, enabled: entry.enabled } };
|
|
2209
2235
|
const better = authenticatorName(it.name, { issuer: entry.issuer, account: entry.account });
|
|
2210
2236
|
if (!isNew && better !== it.name) {
|
|
2211
2237
|
try {
|
|
@@ -2291,7 +2317,11 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
|
|
|
2291
2317
|
/* @__PURE__ */ jsx10(VaultRow, { label: "Website", hint: websiteBad ? /* @__PURE__ */ jsx10("span", { className: "text-destructive", children: "Not a web address" }) : void 0, children: /* @__PURE__ */ jsx10(Field3, { className: "w-full", type: "url", value: website, disabled: busy || created !== null, onChange: (e) => setWebsite(e.target.value), placeholder: "example.com", "aria-label": "Website", "aria-invalid": websiteBad || void 0 }) })
|
|
2292
2318
|
] }) : null
|
|
2293
2319
|
] }),
|
|
2294
|
-
error ? /* @__PURE__ */
|
|
2320
|
+
error ? /* @__PURE__ */ jsxs9("p", { className: "px-1 text-sm text-destructive", role: "alert", children: [
|
|
2321
|
+
error,
|
|
2322
|
+
" ",
|
|
2323
|
+
/* @__PURE__ */ jsx10(Button8, { variant: "quiet", onClick: onChanged, children: "Retry" })
|
|
2324
|
+
] }) : null
|
|
2295
2325
|
]
|
|
2296
2326
|
}
|
|
2297
2327
|
);
|
|
@@ -2326,23 +2356,8 @@ function otpGroups(items, entries, query, hidden = /* @__PURE__ */ new Set()) {
|
|
|
2326
2356
|
const rank = (k) => k === "me" ? 0 : k === "shared" ? 2 : 1;
|
|
2327
2357
|
return [...groups.entries()].sort(([a, x], [b, y]) => rank(a) - rank(b) || x.label.localeCompare(y.label)).map(([key, g]) => ({ key, ...g }));
|
|
2328
2358
|
}
|
|
2329
|
-
function
|
|
2330
|
-
|
|
2331
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2332
|
-
const out = items.map((i) => {
|
|
2333
|
-
seen.add(i.id);
|
|
2334
|
-
const mine = added.get(i.id);
|
|
2335
|
-
return mine && !i.authenticator ? { ...i, authenticator: mine.authenticator, name: mine.name } : i;
|
|
2336
|
-
});
|
|
2337
|
-
for (const [id, a] of added) if (!seen.has(id)) out.push(a);
|
|
2338
|
-
return out;
|
|
2339
|
-
}
|
|
2340
|
-
function AuthenticatorView({ port, host, items: listed, definitions, query, onOpen, onChanged, header }) {
|
|
2341
|
-
const [added, setAdded] = useState12(/* @__PURE__ */ new Map());
|
|
2342
|
-
const items = withAdded(listed, added);
|
|
2343
|
-
useEffect10(() => {
|
|
2344
|
-
if (added.size > 0 && [...added.keys()].every((id) => listed.some((i) => i.id === id && i.authenticator))) setAdded(/* @__PURE__ */ new Map());
|
|
2345
|
-
}, [listed, added]);
|
|
2359
|
+
function AuthenticatorView({ port, host, items, definitions, query, onOpen, onChanged, header }) {
|
|
2360
|
+
const [incomplete, setIncomplete] = useState12(true);
|
|
2346
2361
|
const [entries, setEntries] = useState12(/* @__PURE__ */ new Map());
|
|
2347
2362
|
const [hidden, setHidden] = useState12(/* @__PURE__ */ new Set());
|
|
2348
2363
|
const [adding, setAdding] = useState12(false);
|
|
@@ -2352,13 +2367,20 @@ function AuthenticatorView({ port, host, items: listed, definitions, query, onOp
|
|
|
2352
2367
|
useEffect10(() => {
|
|
2353
2368
|
let live = true;
|
|
2354
2369
|
port.authenticator.list().then(
|
|
2355
|
-
(list) =>
|
|
2356
|
-
|
|
2370
|
+
(list) => {
|
|
2371
|
+
if (live) {
|
|
2372
|
+
setEntries(new Map(list.entries.map((e) => [e.itemId, e])));
|
|
2373
|
+
setIncomplete(list.unavailableScopes.length > 0);
|
|
2374
|
+
}
|
|
2375
|
+
},
|
|
2376
|
+
() => {
|
|
2377
|
+
if (live) setIncomplete(true);
|
|
2378
|
+
}
|
|
2357
2379
|
);
|
|
2358
2380
|
return () => {
|
|
2359
2381
|
live = false;
|
|
2360
2382
|
};
|
|
2361
|
-
}, [port,
|
|
2383
|
+
}, [port, items]);
|
|
2362
2384
|
const groups = otpGroups(items, entries, query, hidden);
|
|
2363
2385
|
const total = items.filter((i) => i.authenticator && !i.deletion && !hidden.has(i.id)).length;
|
|
2364
2386
|
const familyOf2 = (key) => definitions.find((d) => d.key === key)?.family ?? "generic";
|
|
@@ -2371,7 +2393,14 @@ function AuthenticatorView({ port, host, items: listed, definitions, query, onOp
|
|
|
2371
2393
|
] }),
|
|
2372
2394
|
total > 0 ? /* @__PURE__ */ jsx11(Button9, { variant: "outline", icon: /* @__PURE__ */ jsx11(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add" }) : null
|
|
2373
2395
|
] }),
|
|
2374
|
-
total
|
|
2396
|
+
total > 0 && (incomplete || items.some((i) => i.authenticatorState !== "known")) ? /* @__PURE__ */ jsxs10("div", { className: "px-4 text-sm text-muted-foreground", children: [
|
|
2397
|
+
"Some codes unavailable ",
|
|
2398
|
+
/* @__PURE__ */ jsx11(Button9, { variant: "quiet", onClick: onChanged, children: "Retry" })
|
|
2399
|
+
] }) : null,
|
|
2400
|
+
total === 0 && (incomplete || items.some((i) => i.authenticatorState !== "known")) ? /* @__PURE__ */ jsxs10("div", { className: "px-4 py-10 text-center text-sm text-muted-foreground", children: [
|
|
2401
|
+
"Codes unavailable ",
|
|
2402
|
+
/* @__PURE__ */ jsx11(Button9, { variant: "quiet", onClick: onChanged, children: "Retry" })
|
|
2403
|
+
] }) : total === 0 ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 px-6 pb-12 text-center", children: [
|
|
2375
2404
|
/* @__PURE__ */ jsx11("span", { className: "flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx11(ShieldGlyph, { className: "size-6" }) }),
|
|
2376
2405
|
/* @__PURE__ */ jsx11("p", { className: "text-sm font-medium text-foreground", children: "No authenticator codes yet" }),
|
|
2377
2406
|
/* @__PURE__ */ jsx11(Button9, { variant: "primary", icon: /* @__PURE__ */ jsx11(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add authenticator" })
|
|
@@ -2404,7 +2433,6 @@ function AuthenticatorView({ port, host, items: listed, definitions, query, onOp
|
|
|
2404
2433
|
definitions,
|
|
2405
2434
|
onClose: () => setAdding(false),
|
|
2406
2435
|
onChanged,
|
|
2407
|
-
onAdded: (item) => setAdded((cur) => new Map(cur).set(item.id, item)),
|
|
2408
2436
|
onDone: (id) => {
|
|
2409
2437
|
setAdding(false);
|
|
2410
2438
|
onOpen(id);
|
|
@@ -2522,7 +2550,7 @@ function OtpListRow({ port, item, family, entry, onOpen, onOpenInNewTab, onRenam
|
|
|
2522
2550
|
}
|
|
2523
2551
|
|
|
2524
2552
|
// src/fields/SettingsPanels.tsx
|
|
2525
|
-
import { useRef as
|
|
2553
|
+
import { useRef as useRef10, useState as useState13 } from "react";
|
|
2526
2554
|
import { Badge as Badge3, Button as Button10, DisclosureHeader, Field as Field4, SegmentedControl, Select as Select3, Switch } from "@ai-matrx/design-system/controls";
|
|
2527
2555
|
import { confirm as confirm3 } from "@ai-matrx/kit/confirm-opener";
|
|
2528
2556
|
import { GlobeIcon } from "@ai-matrx/design-system/icons";
|
|
@@ -2655,7 +2683,7 @@ function FieldSettings({ item, port, field, onClose, onChanged }) {
|
|
|
2655
2683
|
const { busy, error, setError, save } = useSaver((next) => onChanged(next));
|
|
2656
2684
|
const [envDraft, setEnvDraft] = useState13(field.envKey ?? "");
|
|
2657
2685
|
const [descDraft, setDescDraft] = useState13(field.description);
|
|
2658
|
-
const envRef =
|
|
2686
|
+
const envRef = useRef10(null);
|
|
2659
2687
|
const canEdit = item.capabilities.canEdit;
|
|
2660
2688
|
const envProblem = envKeyProblem(envDraft);
|
|
2661
2689
|
const blocker = sandboxBlocker(envDraft, field.envKey);
|
|
@@ -2819,7 +2847,7 @@ function ItemSettingsPanel({ item, port, onClose, onChanged, onPanel }) {
|
|
|
2819
2847
|
}
|
|
2820
2848
|
|
|
2821
2849
|
// src/generator/PasswordGenerator.tsx
|
|
2822
|
-
import { createContext as createContext3, useContext as useContext3, useEffect as useEffect11, useRef as
|
|
2850
|
+
import { createContext as createContext3, useContext as useContext3, useEffect as useEffect11, useRef as useRef11, useState as useState14 } from "react";
|
|
2823
2851
|
import { Popover, PopoverContent, PopoverTrigger, cn as cn6 } from "@ai-matrx/design-system";
|
|
2824
2852
|
import { Button as Button11, NumberStepper, SegmentedControl as SegmentedControl2, Select as Select4, Switch as Switch2 } from "@ai-matrx/design-system/controls";
|
|
2825
2853
|
import { CopyIcon as CopyIcon6 } from "@ai-matrx/design-system/icons";
|
|
@@ -2897,7 +2925,7 @@ function PasswordGenerator({ limits: source, onUse, useLabel = "Use" }) {
|
|
|
2897
2925
|
const [revealed, setRevealed] = useState14(false);
|
|
2898
2926
|
const [note, setNote] = useState14(null);
|
|
2899
2927
|
const copyValue2 = useCopy();
|
|
2900
|
-
const expiry =
|
|
2928
|
+
const expiry = useRef11(null);
|
|
2901
2929
|
const drop = () => {
|
|
2902
2930
|
if (expiry.current) clearTimeout(expiry.current);
|
|
2903
2931
|
expiry.current = null;
|
|
@@ -3123,11 +3151,11 @@ function organizationHints(orgs) {
|
|
|
3123
3151
|
}
|
|
3124
3152
|
|
|
3125
3153
|
// src/shown-until-move.ts
|
|
3126
|
-
import { useEffect as useEffect12, useRef as
|
|
3154
|
+
import { useEffect as useEffect12, useRef as useRef12, useState as useState15 } from "react";
|
|
3127
3155
|
var keyOf = (loc) => `${String(loc.vault)}|${loc.view}|${loc.itemId ?? ""}|${loc.mode}|${loc.panel ?? ""}|${loc.kind ?? ""}`;
|
|
3128
3156
|
function useShownUntilMove(loc) {
|
|
3129
3157
|
const [shown, setShown] = useState15(null);
|
|
3130
|
-
const home =
|
|
3158
|
+
const home = useRef12(null);
|
|
3131
3159
|
const key = keyOf(loc);
|
|
3132
3160
|
useEffect12(() => {
|
|
3133
3161
|
if (!shown) {
|
|
@@ -3185,7 +3213,7 @@ function useLocked(lock) {
|
|
|
3185
3213
|
|
|
3186
3214
|
// src/io/BackupPanel.tsx
|
|
3187
3215
|
import { formatFileSize } from "@ai-matrx/kit/format";
|
|
3188
|
-
import { useEffect as useEffect13, useRef as
|
|
3216
|
+
import { useEffect as useEffect13, useRef as useRef13, useState as useState17 } from "react";
|
|
3189
3217
|
import { Button as Button13, Field as Field5, SegmentedControl as SegmentedControl3, Switch as Switch3 } from "@ai-matrx/design-system/controls";
|
|
3190
3218
|
import { downloadFile } from "@ai-matrx/kit/download";
|
|
3191
3219
|
import { confirm as confirm4 } from "@ai-matrx/kit/confirm-opener";
|
|
@@ -3342,8 +3370,8 @@ function Omissions({ omissions, accepted, onAccepted, verb }) {
|
|
|
3342
3370
|
function BackupPanel({ port, host, items, onClose, onChanged }) {
|
|
3343
3371
|
const minLength = passphraseMinimum(host.backupPassphraseMinLength);
|
|
3344
3372
|
const confirmed = useConfirmed();
|
|
3345
|
-
const fileInput =
|
|
3346
|
-
const abort =
|
|
3373
|
+
const fileInput = useRef13(null);
|
|
3374
|
+
const abort = useRef13(null);
|
|
3347
3375
|
const [mode, setMode] = useState17("backup");
|
|
3348
3376
|
const [busy, setBusy] = useState17(false);
|
|
3349
3377
|
const [error, setError] = useState17(null);
|
|
@@ -3695,7 +3723,7 @@ function ExportPanel({ port, items, onClose }) {
|
|
|
3695
3723
|
}
|
|
3696
3724
|
|
|
3697
3725
|
// src/io/ImportPanel.tsx
|
|
3698
|
-
import { useEffect as useEffect14, useMemo as useMemo2, useRef as
|
|
3726
|
+
import { useEffect as useEffect14, useMemo as useMemo2, useRef as useRef14, useState as useState19 } from "react";
|
|
3699
3727
|
import { Button as Button15, Select as Select6, Switch as Switch4, Textarea as Textarea2 } from "@ai-matrx/design-system/controls";
|
|
3700
3728
|
|
|
3701
3729
|
// src/io/csv-import.ts
|
|
@@ -4558,10 +4586,10 @@ var ME = "me";
|
|
|
4558
4586
|
function ImportPanel({ port, host, items, definitions, organizations, onClose, onChanged, limits }) {
|
|
4559
4587
|
const confirmed = useConfirmed();
|
|
4560
4588
|
const [otpFor, setOtpFor] = useState19(null);
|
|
4561
|
-
const fileInput =
|
|
4562
|
-
const abort =
|
|
4563
|
-
const stop =
|
|
4564
|
-
const keys =
|
|
4589
|
+
const fileInput = useRef14(null);
|
|
4590
|
+
const abort = useRef14(null);
|
|
4591
|
+
const stop = useRef14(false);
|
|
4592
|
+
const keys = useRef14(/* @__PURE__ */ new Map());
|
|
4565
4593
|
const [source, setSource] = useState19("auto");
|
|
4566
4594
|
const [envText, setEnvText] = useState19("");
|
|
4567
4595
|
const [injectSandbox, setInjectSandbox] = useState19(false);
|
|
@@ -4862,14 +4890,14 @@ function Notices({ draft }) {
|
|
|
4862
4890
|
}
|
|
4863
4891
|
|
|
4864
4892
|
// src/ItemList.tsx
|
|
4865
|
-
import { useEffect as useEffect17, useRef as
|
|
4893
|
+
import { useEffect as useEffect17, useRef as useRef17, useState as useState28 } from "react";
|
|
4866
4894
|
import { DropdownMenu as DropdownMenu4, DropdownMenuContent as DropdownMenuContent4, DropdownMenuItem as DropdownMenuItem4, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator as DropdownMenuSeparator4, DropdownMenuTrigger as DropdownMenuTrigger4 } from "@ai-matrx/design-system";
|
|
4867
4895
|
import { cn as cn11 } from "@ai-matrx/design-system";
|
|
4868
4896
|
import { Button as Button23, SearchField, Tile as Tile3 } from "@ai-matrx/design-system/controls";
|
|
4869
4897
|
import { CopyIcon as CopyIcon8 } from "@ai-matrx/design-system/icons";
|
|
4870
4898
|
|
|
4871
4899
|
// src/ItemPane.tsx
|
|
4872
|
-
import { Fragment as Fragment17, useEffect as useEffect16, useMemo as useMemo3, useRef as
|
|
4900
|
+
import { Fragment as Fragment17, useEffect as useEffect16, useMemo as useMemo3, useRef as useRef16, useState as useState27 } from "react";
|
|
4873
4901
|
import { DropdownMenu as DropdownMenu3, DropdownMenuContent as DropdownMenuContent3, DropdownMenuItem as DropdownMenuItem3, DropdownMenuSeparator as DropdownMenuSeparator3, DropdownMenuTrigger as DropdownMenuTrigger3 } from "@ai-matrx/design-system";
|
|
4874
4902
|
import { cn as cn10 } from "@ai-matrx/design-system";
|
|
4875
4903
|
import { Badge as Badge7, Button as Button22, CONTROLS_CONTAINER_CLASS, OptionCombobox, Switch as Switch6 } from "@ai-matrx/design-system/controls";
|
|
@@ -4877,7 +4905,7 @@ import { confirm as confirm10 } from "@ai-matrx/kit/confirm-opener";
|
|
|
4877
4905
|
|
|
4878
4906
|
// src/activity/FilesSection.tsx
|
|
4879
4907
|
import { formatFileSize as formatFileSize2 } from "@ai-matrx/kit/format";
|
|
4880
|
-
import { useRef as
|
|
4908
|
+
import { useRef as useRef15, useState as useState20 } from "react";
|
|
4881
4909
|
import { Badge as Badge5, Button as Button16, Field as Field6, SegmentedControl as SegmentedControl4 } from "@ai-matrx/design-system/controls";
|
|
4882
4910
|
import { cn as cn7 } from "@ai-matrx/design-system";
|
|
4883
4911
|
import { confirm as confirm6 } from "@ai-matrx/kit/confirm-opener";
|
|
@@ -4899,7 +4927,7 @@ function FilesSection({ item, port, editing, onChanged }) {
|
|
|
4899
4927
|
const [error, setError] = useState20(null);
|
|
4900
4928
|
const [pending, setPending] = useState20(null);
|
|
4901
4929
|
const [dragging, setDragging] = useState20(false);
|
|
4902
|
-
const picker =
|
|
4930
|
+
const picker = useRef15(null);
|
|
4903
4931
|
if (!canAdd && item.attachments.length === 0) return null;
|
|
4904
4932
|
async function run(key, work, fallback) {
|
|
4905
4933
|
setBusy(key);
|
|
@@ -4977,7 +5005,7 @@ function FilesSection({ item, port, editing, onChanged }) {
|
|
|
4977
5005
|
}
|
|
4978
5006
|
function EditableFile({ port, item: it, file: a, busy: b, run: go, onDownload, onTooBig }) {
|
|
4979
5007
|
const [open, setOpen] = useState20(false);
|
|
4980
|
-
const replacer =
|
|
5008
|
+
const replacer = useRef15(null);
|
|
4981
5009
|
async function remove() {
|
|
4982
5010
|
const yes = await confirm6({ title: `Delete ${a.label}?`, description: "The encrypted file is removed from this item.", confirmLabel: "Delete", variant: "destructive" });
|
|
4983
5011
|
if (yes) await go(`delete:${a.id}`, () => port.activity.deleteFile(it, a.id), "The file could not be deleted.");
|
|
@@ -5089,7 +5117,7 @@ function DraftFilesSection({ files, onFiles }) {
|
|
|
5089
5117
|
const [pending, setPending] = useState20(null);
|
|
5090
5118
|
const [error, setError] = useState20(null);
|
|
5091
5119
|
const [dragging, setDragging] = useState20(false);
|
|
5092
|
-
const picker =
|
|
5120
|
+
const picker = useRef15(null);
|
|
5093
5121
|
const choose = (file) => {
|
|
5094
5122
|
if (!file) return;
|
|
5095
5123
|
if (fileTooBig(file.size)) return setError(TOO_BIG);
|
|
@@ -6012,6 +6040,10 @@ function confirmDelete(item) {
|
|
|
6012
6040
|
function ItemPane(props) {
|
|
6013
6041
|
const { port, item, definition, mode, host, organizations, defaultOwner, seed, onBack, onMode, onSaved, onDeleted, onDirty, onCancelNew, onPanel, onChanged, favorite, notice, refreshKey } = props;
|
|
6014
6042
|
const confirmed = useConfirmed();
|
|
6043
|
+
const latestItem = useRef16(item);
|
|
6044
|
+
latestItem.current = item;
|
|
6045
|
+
const [createdForRetry, setCreatedForRetry] = useState27(null);
|
|
6046
|
+
const enrolledForRetry = useRef16(null);
|
|
6015
6047
|
const notify = useNotify();
|
|
6016
6048
|
const editing = mode !== "view";
|
|
6017
6049
|
const seedName = seed?.name ?? "";
|
|
@@ -6044,8 +6076,8 @@ function ItemPane(props) {
|
|
|
6044
6076
|
const [savedNew, setSavedNew] = useState27(false);
|
|
6045
6077
|
const dirty = editing && !savedNew && isDirty(base, draft);
|
|
6046
6078
|
const declared = definition?.fields ?? [];
|
|
6047
|
-
const prev =
|
|
6048
|
-
const paneRef =
|
|
6079
|
+
const prev = useRef16(null);
|
|
6080
|
+
const paneRef = useRef16(null);
|
|
6049
6081
|
const room = useRoomOf(paneRef);
|
|
6050
6082
|
useEffect16(() => {
|
|
6051
6083
|
const was = prev.current;
|
|
@@ -6067,7 +6099,7 @@ function ItemPane(props) {
|
|
|
6067
6099
|
setShowRecovery(false);
|
|
6068
6100
|
setSavedNew(false);
|
|
6069
6101
|
}, [item?.id, mode]);
|
|
6070
|
-
const onDirtyRef =
|
|
6102
|
+
const onDirtyRef = useRef16(onDirty);
|
|
6071
6103
|
onDirtyRef.current = onDirty;
|
|
6072
6104
|
useEffect16(() => onDirtyRef.current(dirty), [dirty]);
|
|
6073
6105
|
const keepKey = draftKey(item?.id ?? null, definition?.key ?? item?.definitionKey ?? null);
|
|
@@ -6097,23 +6129,39 @@ function ItemPane(props) {
|
|
|
6097
6129
|
setError(null);
|
|
6098
6130
|
const out = { ...draft, values: Object.fromEntries(changedValues(base, draft)) };
|
|
6099
6131
|
if (out.values[RECOVERY_CODES_KEY] !== void 0) out.values[RECOVERY_CODES_KEY] = parseRecoveryCodes(out.values[RECOVERY_CODES_KEY]).join("\n");
|
|
6100
|
-
const withOtp = async (saved, notice2) => {
|
|
6132
|
+
const withOtp = async (saved, notice2, freshlyCreated = false) => {
|
|
6101
6133
|
const setup = out.authenticator.trim();
|
|
6102
6134
|
if (!setup) return [saved, notice2, false];
|
|
6103
6135
|
try {
|
|
6104
|
-
|
|
6136
|
+
if (enrolledForRetry.current?.itemId !== saved.id || enrolledForRetry.current.setup !== setup) await confirmed(async () => {
|
|
6137
|
+
const current = freshlyCreated ? saved : await port.get(saved);
|
|
6138
|
+
const visible = latestItem.current?.id === saved.id ? latestItem.current : null;
|
|
6139
|
+
const ownEnrollment = enrolledForRetry.current?.itemId === saved.id;
|
|
6140
|
+
if (!freshlyCreated && (current.authenticatorState !== "known" || !item?.authenticator && current.authenticator && !ownEnrollment || item && !visible) || visible && (visible.authenticatorState !== "known" || !item?.authenticator && visible.authenticator && !ownEnrollment)) throw new Error("Authenticator status unavailable. Retry after refreshing");
|
|
6141
|
+
await port.authenticator.enrol(current, setup);
|
|
6142
|
+
enrolledForRetry.current = { itemId: saved.id, setup };
|
|
6143
|
+
});
|
|
6105
6144
|
return [await port.get(saved), notice2, true];
|
|
6106
6145
|
} catch (e) {
|
|
6146
|
+
if (enrolledForRetry.current?.itemId === saved.id && enrolledForRetry.current.setup === setup) return [saved, `Saved and authenticator set up, but refresh failed: ${e instanceof Error ? e.message : "Retry"}.`, false];
|
|
6107
6147
|
const why = wasCancelled(e) ? "it was not confirmed" : e instanceof Error ? e.message : "it was refused";
|
|
6108
6148
|
return [saved, `Saved, but the authenticator was not set up: ${why}. Add it with Edit.`, false];
|
|
6109
6149
|
}
|
|
6110
6150
|
};
|
|
6111
6151
|
try {
|
|
6112
|
-
if (item) {
|
|
6113
|
-
const r = await port.save(item, definition, out);
|
|
6152
|
+
if (item || createdForRetry) {
|
|
6153
|
+
const r = await port.save(item ?? createdForRetry, definition, out);
|
|
6114
6154
|
const [next, n, enrolled] = await withOtp(r.item, r.notice);
|
|
6115
|
-
|
|
6116
|
-
|
|
6155
|
+
if (out.authenticator.trim() && !enrolled) {
|
|
6156
|
+
setError(n);
|
|
6157
|
+
onChanged();
|
|
6158
|
+
return;
|
|
6159
|
+
}
|
|
6160
|
+
if (createdForRetry) await finishNew(next, n, enrolled);
|
|
6161
|
+
else {
|
|
6162
|
+
dropKeptDraft(keepKey);
|
|
6163
|
+
onSaved(next, false, n, enrolled);
|
|
6164
|
+
}
|
|
6117
6165
|
} else if (definition && owner === "person") {
|
|
6118
6166
|
const secretKey = declared.find((f) => f.storage === "encrypted" && f.kind === "secret")?.key ?? null;
|
|
6119
6167
|
const r = await port.sharing.assign({ recipientEmail: recipient.trim(), definition, draft: out, generateFieldKey: generatePrivately ? secretKey : null });
|
|
@@ -6123,12 +6171,14 @@ function ItemPane(props) {
|
|
|
6123
6171
|
setSent(r);
|
|
6124
6172
|
} else if (definition) {
|
|
6125
6173
|
const r = await port.create(definition, owner === "me" ? null : owner, out);
|
|
6126
|
-
|
|
6127
|
-
const [
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6174
|
+
setCreatedForRetry(r.item);
|
|
6175
|
+
const [withCode, n0, enrolled] = await withOtp(r.item, r.notice, true);
|
|
6176
|
+
if (out.authenticator.trim() && !enrolled) {
|
|
6177
|
+
setError(n0);
|
|
6178
|
+
onChanged();
|
|
6179
|
+
return;
|
|
6180
|
+
}
|
|
6181
|
+
await finishNew(withCode, n0, enrolled);
|
|
6132
6182
|
}
|
|
6133
6183
|
} catch (e) {
|
|
6134
6184
|
setError(e instanceof Error ? e.message : "Saving failed. Nothing was changed.");
|
|
@@ -6136,6 +6186,13 @@ function ItemPane(props) {
|
|
|
6136
6186
|
setBusy(false);
|
|
6137
6187
|
}
|
|
6138
6188
|
}
|
|
6189
|
+
async function finishNew(saved, notice2, enrolled) {
|
|
6190
|
+
const [next, finalNotice] = await storeQueuedFiles(saved, notice2);
|
|
6191
|
+
dropKeptDraft(keepKey);
|
|
6192
|
+
setSavedNew(true);
|
|
6193
|
+
onDirty(false);
|
|
6194
|
+
onSaved(next, true, finalNotice, enrolled);
|
|
6195
|
+
}
|
|
6139
6196
|
async function storeQueuedFiles(saved, notice2) {
|
|
6140
6197
|
let next = saved;
|
|
6141
6198
|
const failed = [];
|
|
@@ -6317,7 +6374,11 @@ function ItemPane(props) {
|
|
|
6317
6374
|
const filesProps = item ? { item, port, host, definition, organizations, arg: null, onClose: () => void 0, onChanged, onPanel } : null;
|
|
6318
6375
|
return /* @__PURE__ */ jsx25(RoomProvider, { value: room, children: /* @__PURE__ */ jsxs23("article", { ref: paneRef, "aria-label": item?.name ?? "New item", className: cn10(CONTROLS_CONTAINER_CLASS, "flex h-full min-h-0 flex-col bg-background"), children: [
|
|
6319
6376
|
header,
|
|
6320
|
-
error ? /* @__PURE__ */
|
|
6377
|
+
error ? /* @__PURE__ */ jsxs23("div", { role: "alert", className: "border-b border-border bg-muted px-5 py-2 text-sm text-destructive", children: [
|
|
6378
|
+
error,
|
|
6379
|
+
" ",
|
|
6380
|
+
error.includes("Retry after refreshing") ? /* @__PURE__ */ jsx25(Button22, { variant: "quiet", onClick: () => onChanged(), children: "Retry" }) : null
|
|
6381
|
+
] }) : null,
|
|
6321
6382
|
notice && !editing ? /* @__PURE__ */ jsx25("div", { role: "status", className: "border-b border-border bg-muted px-5 py-2 text-sm text-warning", children: notice }) : null,
|
|
6322
6383
|
item && !editing && (itemOff || offFields.length > 0) ? /* @__PURE__ */ jsxs23("div", { role: "status", className: "flex items-center gap-3 border-b border-border bg-muted px-5 py-2 text-sm", children: [
|
|
6323
6384
|
/* @__PURE__ */ jsx25("span", { className: "min-w-0 flex-1 truncate text-warning", children: offFields.length === 0 ? "This item is turned off" : offFields.length === 1 ? "1 field is turned off" : `${offFields.length} fields are turned off` }),
|
|
@@ -6421,7 +6482,11 @@ function ItemPane(props) {
|
|
|
6421
6482
|
i
|
|
6422
6483
|
)),
|
|
6423
6484
|
/* @__PURE__ */ jsx25(AddRow, { label: draft.loginUrls.length === 0 ? "Website" : "", onAdd: () => patch({ loginUrls: [...draft.loginUrls, ""] }), children: draft.loginUrls.length === 0 ? "Add website" : "Add another website" }),
|
|
6424
|
-
/* @__PURE__ */ jsx25(TotpEditRow, { value: draft.authenticator, onChange: (v) => patch({ authenticator: v }), hasExisting: Boolean(item?.authenticator || seedField), item, port, onEnrolled: onChanged }),
|
|
6485
|
+
/* @__PURE__ */ jsx25("div", { hidden: Boolean(item && item.authenticatorState !== "known"), children: /* @__PURE__ */ jsx25(TotpEditRow, { value: draft.authenticator, onChange: (v) => patch({ authenticator: v }), hasExisting: Boolean(item?.authenticator || seedField), item, port, onEnrolled: onChanged }) }),
|
|
6486
|
+
item && item.authenticatorState !== "known" ? /* @__PURE__ */ jsxs23(VaultRow, { label: "Authenticator", children: [
|
|
6487
|
+
/* @__PURE__ */ jsx25("span", { className: "text-muted-foreground", children: item.authenticatorState === "pending" ? "Loading" : "Unavailable" }),
|
|
6488
|
+
item.authenticatorState === "unavailable" ? /* @__PURE__ */ jsx25(Button22, { variant: "quiet", onClick: () => onChanged?.(), children: "Retry" }) : null
|
|
6489
|
+
] }) : null,
|
|
6425
6490
|
item && isLogin && !item.authenticator ? /* @__PURE__ */ jsx25(TwoFactorMethodRow, { port, item, onChanged }) : null,
|
|
6426
6491
|
!item && isLogin && draft.authenticator.trim() === "" ? /* @__PURE__ */ jsx25(DraftTwoFactorRow, { custom: draft.custom, onCustom: (custom) => patch({ custom }) }) : null,
|
|
6427
6492
|
recoveryEditing ? /* @__PURE__ */ jsx25(
|
|
@@ -6500,7 +6565,10 @@ function ItemPane(props) {
|
|
|
6500
6565
|
}),
|
|
6501
6566
|
extraFields.map((f) => isRecoveryCodesField(f) ? /* @__PURE__ */ jsx25(RecoveryCodesRow, { item, field: f, port, onChanged }, f.id) : /* @__PURE__ */ jsx25(SecretRow, { port, item, field: f, onTurnOn: turnOnOne(f), extra: fieldSettings(f) }, f.id)),
|
|
6502
6567
|
item.loginUrls.length > 0 ? item.loginUrls.map((u, i) => /* @__PURE__ */ jsx25(PlainRow, { label: i === 0 ? "Website" : `Website ${i + 1}`, value: u, openUrl: host.openUrl }, `${u}-${i}`)) : /* @__PURE__ */ jsx25(AddRow, { label: "Website", onAdd: canEdit ? () => onMode("edit") : null, children: "Add website" }),
|
|
6503
|
-
item.authenticator ? /* @__PURE__ */ jsx25(TotpRow, { port, item, onChanged }) :
|
|
6568
|
+
item.authenticator ? /* @__PURE__ */ jsx25(TotpRow, { port, item, onChanged }) : item.authenticatorState !== "known" ? /* @__PURE__ */ jsxs23(VaultRow, { label: "Authenticator", children: [
|
|
6569
|
+
/* @__PURE__ */ jsx25("span", { className: "text-muted-foreground", children: item.authenticatorState === "pending" ? "Loading" : "Unavailable" }),
|
|
6570
|
+
item.authenticatorState === "unavailable" ? /* @__PURE__ */ jsx25(Button22, { variant: "quiet", onClick: () => onChanged?.(), children: "Retry" }) : null
|
|
6571
|
+
] }) : seedField && !seedField.active ? (
|
|
6504
6572
|
// The secret is still there, only off: turning it on brings the codes back. Never "Set up",
|
|
6505
6573
|
// which would invite replacing a working two-factor secret.
|
|
6506
6574
|
/* @__PURE__ */ jsx25(VaultRow, { label: "Authenticator", actions: canTurnOn ? /* @__PURE__ */ jsx25(TurnOnButton, { label: "Authenticator", onTurnOn: () => turnOn([seedField]) }) : null, children: /* @__PURE__ */ jsx25("span", { className: "text-muted-foreground", children: "Turned off" }) })
|
|
@@ -6577,7 +6645,7 @@ function ItemList({ title, items, definitions, port, selectedId, onSelect, query
|
|
|
6577
6645
|
const pinned = matchingIds ? items.filter((i) => matchingIds.has(i.id)) : [];
|
|
6578
6646
|
const rest = matchingIds ? items.filter((i) => !matchingIds.has(i.id)) : items;
|
|
6579
6647
|
const ordered = [...pinned, ...rest];
|
|
6580
|
-
const listRef =
|
|
6648
|
+
const listRef = useRef17(null);
|
|
6581
6649
|
useEffect17(() => {
|
|
6582
6650
|
if (selectedId) listRef.current?.querySelector(`[data-item="${selectedId}"]`)?.scrollIntoView({ block: "nearest" });
|
|
6583
6651
|
}, [selectedId, items.length]);
|
|
@@ -6681,7 +6749,7 @@ function ListRow({ item, family, port, on, onSelect, withOwner, ownContextMenu,
|
|
|
6681
6749
|
...manageEntries(item, onAction),
|
|
6682
6750
|
...hostEntries.map((h) => ({ ...h, kind: h.kind ?? "host" }))
|
|
6683
6751
|
];
|
|
6684
|
-
const entriesRef =
|
|
6752
|
+
const entriesRef = useRef17(entries);
|
|
6685
6753
|
entriesRef.current = entries;
|
|
6686
6754
|
const glyphOf = (e) => e.id === "copy:username" ? /* @__PURE__ */ jsx26(UserGlyph, { className: "size-4" }) : e.id === "copy:password" ? /* @__PURE__ */ jsx26(KeyGlyph, { className: "size-4" }) : e.id === "copy:code" ? /* @__PURE__ */ jsx26(ClockGlyph, { className: "size-4" }) : e.kind === "favorite" ? /* @__PURE__ */ jsx26(StarGlyph, { className: cn11("size-4", favorite?.on && "fill-current text-warning") }) : e.kind === "open" ? /* @__PURE__ */ jsx26(ChevronRightGlyph, { className: "size-4" }) : e.kind === "host" || e.kind === "manage" ? null : /* @__PURE__ */ jsx26(CopyIcon8, { className: "size-4" });
|
|
6687
6755
|
const menuItem = (e) => /* @__PURE__ */ jsxs24(DropdownMenuItem4, { disabled: e.disabled ?? false, onSelect: e.onSelect, children: [
|
|
@@ -7088,7 +7156,7 @@ function NewItemFlow({ definitions, url, onUrl, onChoose, onCancel }) {
|
|
|
7088
7156
|
}
|
|
7089
7157
|
|
|
7090
7158
|
// src/sharing/SecureLinkPanel.tsx
|
|
7091
|
-
import { useEffect as useEffect19, useRef as
|
|
7159
|
+
import { useEffect as useEffect19, useRef as useRef18, useState as useState32 } from "react";
|
|
7092
7160
|
import { confirm as confirm11 } from "@ai-matrx/kit/confirm-opener";
|
|
7093
7161
|
import { Checkbox as Checkbox3 } from "@ai-matrx/design-system";
|
|
7094
7162
|
import { Badge as Badge8, Button as Button27, Field as Field11, RegionSkeleton as RegionSkeleton2, SegmentedControl as SegmentedControl6, Textarea as Textarea4 } from "@ai-matrx/design-system/controls";
|
|
@@ -7213,7 +7281,7 @@ function SecureLinkPanel({ item, port, onClose }) {
|
|
|
7213
7281
|
};
|
|
7214
7282
|
}, [sharing, item, nonce]);
|
|
7215
7283
|
const [phoneTyped, setPhoneTyped] = useState32(false);
|
|
7216
|
-
const prefilled =
|
|
7284
|
+
const prefilled = useRef18(false);
|
|
7217
7285
|
useEffect19(() => {
|
|
7218
7286
|
const lookup = sharing.phoneOnFile;
|
|
7219
7287
|
if (!lookup || phoneTyped) return;
|
|
@@ -7690,19 +7758,19 @@ async function loadFullPreview(bridge, operationId, current) {
|
|
|
7690
7758
|
}
|
|
7691
7759
|
|
|
7692
7760
|
// src/native/use-native-import.ts
|
|
7693
|
-
import { useEffect as useEffect21, useRef as
|
|
7761
|
+
import { useEffect as useEffect21, useRef as useRef19, useState as useState34 } from "react";
|
|
7694
7762
|
var fail = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
|
|
7695
7763
|
var RESTART = "Your account changed. The import was stopped; start it again.";
|
|
7696
7764
|
function useNativeImport(bridge, stillAllowed, onImported) {
|
|
7697
7765
|
const [screen, setScreen] = useState34({ kind: "idle", message: null });
|
|
7698
7766
|
const [busy, setBusy] = useState34(false);
|
|
7699
|
-
const generation =
|
|
7700
|
-
const mounted =
|
|
7701
|
-
const op =
|
|
7702
|
-
const last =
|
|
7703
|
-
const timer2 =
|
|
7704
|
-
const polls =
|
|
7705
|
-
const refreshRef =
|
|
7767
|
+
const generation = useRef19(0);
|
|
7768
|
+
const mounted = useRef19(false);
|
|
7769
|
+
const op = useRef19(null);
|
|
7770
|
+
const last = useRef19(null);
|
|
7771
|
+
const timer2 = useRef19(null);
|
|
7772
|
+
const polls = useRef19(0);
|
|
7773
|
+
const refreshRef = useRef19(async () => void 0);
|
|
7706
7774
|
const current = (g) => mounted.current && g === generation.current;
|
|
7707
7775
|
const stopPolling = () => {
|
|
7708
7776
|
if (timer2.current !== null) clearTimeout(timer2.current);
|
|
@@ -7871,7 +7939,7 @@ function useNativeImport(bridge, stillAllowed, onImported) {
|
|
|
7871
7939
|
setBusy(false);
|
|
7872
7940
|
setScreen({ kind: "idle", message: null });
|
|
7873
7941
|
};
|
|
7874
|
-
const invalidateRef =
|
|
7942
|
+
const invalidateRef = useRef19(invalidate);
|
|
7875
7943
|
invalidateRef.current = invalidate;
|
|
7876
7944
|
useEffect21(() => {
|
|
7877
7945
|
mounted.current = true;
|
|
@@ -8043,13 +8111,13 @@ function CaptureCard({ capture, onChanged }) {
|
|
|
8043
8111
|
}
|
|
8044
8112
|
|
|
8045
8113
|
// src/extension/FillSetupCard.tsx
|
|
8046
|
-
import { useRef as
|
|
8114
|
+
import { useRef as useRef20, useState as useState37, useSyncExternalStore as useSyncExternalStore4 } from "react";
|
|
8047
8115
|
import { Button as Button31, Field as Field12 } from "@ai-matrx/design-system/controls";
|
|
8048
8116
|
import { jsx as jsx34, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
8049
8117
|
function FillSetupCard({ fillSetup }) {
|
|
8050
8118
|
const notify = useNotify();
|
|
8051
8119
|
const state = useSyncExternalStore4(fillSetup.subscribe, fillSetup.get, fillSetup.get);
|
|
8052
|
-
const box =
|
|
8120
|
+
const box = useRef20(null);
|
|
8053
8121
|
const [busy, setBusy] = useState37(false);
|
|
8054
8122
|
const [error, setError] = useState37(null);
|
|
8055
8123
|
if (state.status !== "off") return null;
|
|
@@ -8589,7 +8657,7 @@ function LockedScreen({ lock }) {
|
|
|
8589
8657
|
] });
|
|
8590
8658
|
}
|
|
8591
8659
|
function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocationChange, onInventory }) {
|
|
8592
|
-
const rootRef =
|
|
8660
|
+
const rootRef = useRef21(null);
|
|
8593
8661
|
const width = useElementWidth(rootRef);
|
|
8594
8662
|
const mode = layout !== "auto" ? layout : width >= FULL_AT ? "full" : width >= SPLIT_AT ? "split" : "stack";
|
|
8595
8663
|
const notify = useNotify();
|
|
@@ -8601,7 +8669,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8601
8669
|
const [error, setError] = useState41(null);
|
|
8602
8670
|
const [inner, setInner] = useState41(DEFAULT_LOCATION);
|
|
8603
8671
|
const loc = location ?? inner;
|
|
8604
|
-
const dirty =
|
|
8672
|
+
const dirty = useRef21(false);
|
|
8605
8673
|
const [notice, setNotice] = useState41(null);
|
|
8606
8674
|
const [firstCode, setFirstCode] = useShownUntilMove(loc);
|
|
8607
8675
|
const move = useCallback4(
|
|
@@ -8630,7 +8698,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8630
8698
|
},
|
|
8631
8699
|
[loc, move]
|
|
8632
8700
|
);
|
|
8633
|
-
const keyState =
|
|
8701
|
+
const keyState = useRef21({ loc, go });
|
|
8634
8702
|
keyState.current = { loc, go };
|
|
8635
8703
|
useEffect23(() => {
|
|
8636
8704
|
const onKey = (e) => {
|
|
@@ -8654,7 +8722,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8654
8722
|
document.addEventListener("keydown", onKey);
|
|
8655
8723
|
return () => document.removeEventListener("keydown", onKey);
|
|
8656
8724
|
}, []);
|
|
8657
|
-
const keepSearchFocus =
|
|
8725
|
+
const keepSearchFocus = useRef21(false);
|
|
8658
8726
|
useEffect23(() => {
|
|
8659
8727
|
if (!keepSearchFocus.current) return;
|
|
8660
8728
|
keepSearchFocus.current = false;
|
|
@@ -8664,13 +8732,14 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8664
8732
|
search.setSelectionRange(search.value.length, search.value.length);
|
|
8665
8733
|
}, [loc.q]);
|
|
8666
8734
|
const hasPrompts = useHostPrompts(host.capture, host.fillSetup);
|
|
8667
|
-
const knobsRef =
|
|
8735
|
+
const knobsRef = useRef21(host.knobs);
|
|
8668
8736
|
knobsRef.current = host.knobs;
|
|
8669
8737
|
const [signedOut, setSignedOut] = useState41(false);
|
|
8670
8738
|
const [refreshError, setRefreshError] = useState41(null);
|
|
8671
8739
|
const [refreshing, setRefreshing] = useState41(false);
|
|
8672
|
-
const loadedAt =
|
|
8673
|
-
const inFlight =
|
|
8740
|
+
const loadedAt = useRef21(0);
|
|
8741
|
+
const inFlight = useRef21(false);
|
|
8742
|
+
const loadGeneration = useRef21(0);
|
|
8674
8743
|
const apply = useCallback4(
|
|
8675
8744
|
(s) => {
|
|
8676
8745
|
loadedAt.current = Date.now();
|
|
@@ -8687,11 +8756,15 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8687
8756
|
const refresh = useCallback4(() => {
|
|
8688
8757
|
if (inFlight.current) return;
|
|
8689
8758
|
inFlight.current = true;
|
|
8759
|
+
const generation = ++loadGeneration.current;
|
|
8690
8760
|
setRefreshing(true);
|
|
8691
8761
|
port.load().then((s) => {
|
|
8762
|
+
if (generation !== loadGeneration.current) return;
|
|
8692
8763
|
setRefreshError(null);
|
|
8693
8764
|
apply(s);
|
|
8694
|
-
}).catch((e) =>
|
|
8765
|
+
}).catch((e) => {
|
|
8766
|
+
if (generation === loadGeneration.current) setRefreshError(plainLoadError(e instanceof Error && e.message ? e.message : "The vault could not be read."));
|
|
8767
|
+
}).finally(() => {
|
|
8695
8768
|
inFlight.current = false;
|
|
8696
8769
|
setRefreshing(false);
|
|
8697
8770
|
});
|
|
@@ -8699,18 +8772,20 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8699
8772
|
port.lists.recents?.list().then(setRecents, () => void 0);
|
|
8700
8773
|
}, [port, apply]);
|
|
8701
8774
|
const load = useCallback4(() => {
|
|
8775
|
+
const generation = ++loadGeneration.current;
|
|
8702
8776
|
setError(null);
|
|
8703
8777
|
setRefreshError(null);
|
|
8704
8778
|
let finished = false;
|
|
8705
8779
|
port.load((partial) => {
|
|
8706
|
-
if (finished) return;
|
|
8780
|
+
if (finished || generation !== loadGeneration.current) return;
|
|
8707
8781
|
setSnap(partial);
|
|
8708
8782
|
setItems(partial.items);
|
|
8709
8783
|
setDeleted(partial.deleted);
|
|
8710
8784
|
}).then((s) => {
|
|
8711
8785
|
finished = true;
|
|
8712
|
-
apply(s);
|
|
8786
|
+
if (generation === loadGeneration.current) apply(s);
|
|
8713
8787
|
}).catch((e) => {
|
|
8788
|
+
if (generation !== loadGeneration.current) return;
|
|
8714
8789
|
setSignedOut(e?.status === 401);
|
|
8715
8790
|
setError(e instanceof Error ? e.message : "The vault could not be loaded.");
|
|
8716
8791
|
});
|
|
@@ -8719,7 +8794,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8719
8794
|
port.lists.recents?.list().then(setRecents, () => void 0);
|
|
8720
8795
|
}, [port, apply]);
|
|
8721
8796
|
useEffect23(load, [load]);
|
|
8722
|
-
const quietRef =
|
|
8797
|
+
const quietRef = useRef21({ loc, ready: false });
|
|
8723
8798
|
quietRef.current = { loc, ready: snap !== null && !error };
|
|
8724
8799
|
const [knobSeconds, setKnobSeconds] = useState41(null);
|
|
8725
8800
|
useEffect23(() => {
|
|
@@ -8755,7 +8830,7 @@ function Workspace({ port, host = NO_HOST, layout = "auto", location, onLocation
|
|
|
8755
8830
|
if (timer2) clearInterval(timer2);
|
|
8756
8831
|
};
|
|
8757
8832
|
}, [refresh, everySeconds]);
|
|
8758
|
-
const inventoryRef =
|
|
8833
|
+
const inventoryRef = useRef21(onInventory);
|
|
8759
8834
|
inventoryRef.current = onInventory;
|
|
8760
8835
|
useEffect23(() => {
|
|
8761
8836
|
if (snap) inventoryRef.current?.({ items, definitions: snap.definitions });
|
|
@@ -9329,7 +9404,7 @@ function createAuthenticatorPort(ctx) {
|
|
|
9329
9404
|
return {
|
|
9330
9405
|
async list() {
|
|
9331
9406
|
const r = await apps.listAuthenticators(api);
|
|
9332
|
-
return (r.entries ?? []).map(entryOf);
|
|
9407
|
+
return { entries: (r.entries ?? []).map(entryOf), unavailableScopes: (r.unavailable_scopes ?? []).map((s) => ({ type: s.type, organizationId: s.organization_id ?? null })) };
|
|
9333
9408
|
},
|
|
9334
9409
|
async get(item) {
|
|
9335
9410
|
try {
|
|
@@ -9543,6 +9618,7 @@ function trashItem(r, ctx) {
|
|
|
9543
9618
|
fields: [],
|
|
9544
9619
|
custom: [],
|
|
9545
9620
|
authenticator: null,
|
|
9621
|
+
authenticatorState: "known",
|
|
9546
9622
|
capabilities: { canUse: false, canEdit: false, canReveal: false, canManage: true },
|
|
9547
9623
|
attachments: [],
|
|
9548
9624
|
uriMatchMode: "host",
|
|
@@ -9899,7 +9975,8 @@ function fieldOf(w, def) {
|
|
|
9899
9975
|
active: w.is_active ?? true
|
|
9900
9976
|
};
|
|
9901
9977
|
}
|
|
9902
|
-
|
|
9978
|
+
var scopeUnavailable = (owner, scopes) => scopes.some((s) => s.type === (owner.kind === "me" ? "user" : owner.kind === "organization" ? "organization" : "shared") && (s.type !== "organization" || !s.organization_id || s.organization_id === owner.organizationId));
|
|
9979
|
+
function itemOf(w, owner, defs, auth, state = "known", unavailable = [], inventoryOwner = owner) {
|
|
9903
9980
|
const def = defs.get(w.definition_key ?? "");
|
|
9904
9981
|
const a = auth.get(w.id);
|
|
9905
9982
|
const cap = w.capabilities ?? {};
|
|
@@ -9917,6 +9994,7 @@ function itemOf(w, owner, defs, auth) {
|
|
|
9917
9994
|
fields: (w.fields ?? []).filter((f) => f.execution_purpose !== "passkey_private").map((f) => fieldOf(f, def)),
|
|
9918
9995
|
custom: (w.non_secret_fields ?? []).map((c) => ({ key: c["key"] ?? "", label: c["label"] ?? humanize(c["key"] ?? ""), value: c["value"] ?? "" })),
|
|
9919
9996
|
authenticator: a ? { digits: a.digits, period: a.period, enabled: a.enabled } : null,
|
|
9997
|
+
authenticatorState: a ? "known" : state === "pending" ? "pending" : state === "unavailable" || scopeUnavailable(inventoryOwner, unavailable) || (w.fields ?? []).some((f) => f.field_key === OTP_SEED_KEY && f.is_active !== false) ? "unavailable" : "known",
|
|
9920
9998
|
capabilities: { canUse: cap.can_use ?? false, canEdit: cap.can_edit ?? false, canReveal: cap.can_reveal ?? false, canManage: cap.can_manage ?? false },
|
|
9921
9999
|
attachments: (w.attachments ?? []).map((a2) => ({ id: a2.id, label: a2.label, description: a2.description ?? "", fileName: a2.file_name, mediaType: a2.media_type ?? "application/octet-stream", sizeBytes: a2.size_bytes, handling: handlingOf(a2.handling), createdAt: a2.created_at, updatedAt: a2.updated_at })),
|
|
9922
10000
|
uriMatchMode: MATCH_MODES2.find((m) => m === w.uri_match_mode) ?? "host",
|
|
@@ -9953,20 +10031,19 @@ function createApiVaultPort(hostApi, options = {}) {
|
|
|
9953
10031
|
};
|
|
9954
10032
|
let defs = /* @__PURE__ */ new Map();
|
|
9955
10033
|
let auth = /* @__PURE__ */ new Map();
|
|
10034
|
+
let authState = "pending";
|
|
10035
|
+
let authUnavailable = [];
|
|
10036
|
+
let inventoryOwners = /* @__PURE__ */ new Map();
|
|
9956
10037
|
let orgs = [];
|
|
10038
|
+
let loadGeneration = 0;
|
|
10039
|
+
let authMutation = 0;
|
|
9957
10040
|
const orgOf = (id, userId, mine) => {
|
|
9958
10041
|
if (id) return { kind: "organization", label: orgs.find((o) => o.id === id)?.name ?? "Organization", organizationId: id };
|
|
9959
10042
|
return mine || userId ? { kind: "me", label: "Personal", organizationId: null } : { kind: "shared", label: "Shared with me", organizationId: null };
|
|
9960
10043
|
};
|
|
9961
|
-
const ownerInOrgList = (w, via, meId) => {
|
|
9962
|
-
if (w.organization_id) return orgOf(w.organization_id, null, false);
|
|
9963
|
-
if (w.user_id && meId && w.user_id === meId) return { kind: "me", label: "Personal", organizationId: null };
|
|
9964
|
-
if (via) return { kind: "organization", label: via.name, organizationId: via.id, memberShared: { userId: w.user_id ?? null } };
|
|
9965
|
-
return { kind: "shared", label: "Shared with me", organizationId: null };
|
|
9966
|
-
};
|
|
9967
10044
|
async function refetch(id, owner) {
|
|
9968
10045
|
const w = await account9.getItem(api, { path: { item_id: id } });
|
|
9969
|
-
return itemOf(w, owner, defs, auth);
|
|
10046
|
+
return itemOf(w, owner, defs, auth, authState, authUnavailable, inventoryOwners.get(id) ?? owner);
|
|
9970
10047
|
}
|
|
9971
10048
|
const visibleCache = /* @__PURE__ */ new Map();
|
|
9972
10049
|
const ctx = {
|
|
@@ -9976,6 +10053,7 @@ function createApiVaultPort(hostApi, options = {}) {
|
|
|
9976
10053
|
definition: (key) => defs.get(key),
|
|
9977
10054
|
organizations: () => orgs,
|
|
9978
10055
|
setAuthenticator: (itemId, entry) => {
|
|
10056
|
+
authMutation++;
|
|
9979
10057
|
const next = new Map(auth);
|
|
9980
10058
|
if (entry) next.set(itemId, entry);
|
|
9981
10059
|
else next.delete(itemId);
|
|
@@ -9993,124 +10071,155 @@ function createApiVaultPort(hostApi, options = {}) {
|
|
|
9993
10071
|
fields: createFieldsPort(ctx),
|
|
9994
10072
|
lists: createListsPort(ctx, options.userState),
|
|
9995
10073
|
async load(onProgress) {
|
|
9996
|
-
const
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
const deleted = /* @__PURE__ */ new Map();
|
|
10018
|
-
const take = (w, owner) => {
|
|
10019
|
-
if (seen.has(w.id) || deleted.has(w.id) || isHiddenItem(w)) return;
|
|
10020
|
-
const item = itemOf(w, owner, defs, auth);
|
|
10021
|
-
(isDeleted(w) ? deleted : seen).set(w.id, item);
|
|
10074
|
+
const run = async (restarts) => {
|
|
10075
|
+
const generation = ++loadGeneration;
|
|
10076
|
+
const mutationAtStart = authMutation;
|
|
10077
|
+
let localAuth = /* @__PURE__ */ new Map();
|
|
10078
|
+
let localState = "pending";
|
|
10079
|
+
let localUnavailable = [];
|
|
10080
|
+
let localDefs = /* @__PURE__ */ new Map();
|
|
10081
|
+
let localOrgs = [];
|
|
10082
|
+
const localOwners = /* @__PURE__ */ new Map();
|
|
10083
|
+
const catalogP = retrying(() => apps2.getAppCatalogKind(api, { path: { app: "matrx", kind: "credential_definition" } }));
|
|
10084
|
+
const authP = retrying(() => apps2.listAuthenticators(api));
|
|
10085
|
+
const userP = retrying(() => account9.listItems(api, { query: { principal_type: "user" } }));
|
|
10086
|
+
const sharedP = retrying(() => account9.sharedWithMe(api));
|
|
10087
|
+
const quiet = (p) => void p.catch(() => void 0);
|
|
10088
|
+
[catalogP, authP, userP, sharedP].forEach(quiet);
|
|
10089
|
+
const orgItemsP = retrying(() => account9.organizationItems(api));
|
|
10090
|
+
quiet(orgItemsP);
|
|
10091
|
+
const raw = {
|
|
10092
|
+
orgLists: [],
|
|
10093
|
+
orgsKnown: options.organizations !== void 0,
|
|
10094
|
+
meId: null
|
|
10022
10095
|
};
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10035
|
-
|
|
10036
|
-
|
|
10096
|
+
const unavailable = [];
|
|
10097
|
+
let done = false;
|
|
10098
|
+
const build = () => {
|
|
10099
|
+
const catalog2 = raw.catalog ?? [];
|
|
10100
|
+
localDefs = 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]));
|
|
10101
|
+
const mineItems = raw.mine?.items ?? [];
|
|
10102
|
+
const meId = raw.meId ?? mineItems.find((w) => w.user_id)?.user_id ?? null;
|
|
10103
|
+
const seen = /* @__PURE__ */ new Map();
|
|
10104
|
+
const deleted = /* @__PURE__ */ new Map();
|
|
10105
|
+
const localOrgOf = (id, mine2) => id ? { kind: "organization", label: localOrgs.find((o) => o.id === id)?.name ?? "Organization", organizationId: id } : mine2 ? { kind: "me", label: "Personal", organizationId: null } : { kind: "shared", label: "Shared with me", organizationId: null };
|
|
10106
|
+
const take = (w, owner, inventoryOwner = owner) => {
|
|
10107
|
+
if (seen.has(w.id) || deleted.has(w.id) || isHiddenItem(w)) return;
|
|
10108
|
+
localOwners.set(w.id, inventoryOwner);
|
|
10109
|
+
const item = itemOf(w, owner, localDefs, localAuth, localState, localUnavailable, inventoryOwner);
|
|
10110
|
+
(isDeleted(w) ? deleted : seen).set(w.id, item);
|
|
10111
|
+
};
|
|
10112
|
+
for (const w of mineItems) take(w, localOrgOf(w.organization_id, true));
|
|
10113
|
+
raw.orgLists.forEach((l, i) => {
|
|
10114
|
+
const via = localOrgs[i];
|
|
10115
|
+
for (const w of l?.items ?? []) take(w, w.organization_id ? localOrgOf(w.organization_id, false) : w.user_id && meId && w.user_id === meId ? localOrgOf(null, true) : via ? { kind: "organization", label: via.name, organizationId: via.id, memberShared: { userId: w.user_id ?? null } } : localOrgOf(null, false));
|
|
10116
|
+
});
|
|
10117
|
+
for (const w of raw.shared?.items ?? []) take(w, w.organization_id ? localOrgOf(w.organization_id, false) : { kind: "shared", label: "Shared with me", organizationId: null }, { kind: "shared", label: "Shared with me", organizationId: null });
|
|
10118
|
+
const pending = done ? [] : [...raw.orgsKnown ? localOrgs.filter((_, i) => raw.orgLists[i] === void 0).map((o) => o.name) : ["your organizations"], ...raw.shared ? [] : ["Shared with me"]];
|
|
10119
|
+
return {
|
|
10120
|
+
unavailable: [...unavailable],
|
|
10121
|
+
...pending.length > 0 ? { pending } : {},
|
|
10122
|
+
items: [...seen.values()],
|
|
10123
|
+
deleted: [...deleted.values()],
|
|
10124
|
+
organizations: localOrgs,
|
|
10125
|
+
definitions: [...localDefs.values()].sort((a, b) => a.sortOrder - b.sortOrder || a.label.localeCompare(b.label))
|
|
10126
|
+
};
|
|
10037
10127
|
};
|
|
10038
|
-
|
|
10039
|
-
|
|
10040
|
-
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
|
|
10044
|
-
|
|
10045
|
-
|
|
10046
|
-
|
|
10047
|
-
|
|
10048
|
-
|
|
10049
|
-
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10128
|
+
const progress = () => {
|
|
10129
|
+
if (!done && generation === loadGeneration && mutationAtStart === authMutation && onProgress && raw.catalog && raw.mine) onProgress(build());
|
|
10130
|
+
};
|
|
10131
|
+
const orgReportP = options.organizations ? null : retrying(() => platform.listMyOrganizations(api));
|
|
10132
|
+
if (orgReportP) quiet(orgReportP);
|
|
10133
|
+
const orgListsReady = (async () => {
|
|
10134
|
+
const report = orgReportP ? await orgReportP : null;
|
|
10135
|
+
raw.meId = report?.user_id ?? null;
|
|
10136
|
+
localOrgs = [...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()];
|
|
10137
|
+
raw.orgLists = localOrgs.map(() => void 0);
|
|
10138
|
+
raw.orgsKnown = true;
|
|
10139
|
+
progress();
|
|
10140
|
+
const one = await orgItemsP.then(
|
|
10141
|
+
(r) => r,
|
|
10142
|
+
(e) => {
|
|
10143
|
+
if (e?.status === 404) {
|
|
10144
|
+
hostProblem("GET /vault/organization-items answered 404; reading each organization vault separately", e);
|
|
10145
|
+
return null;
|
|
10146
|
+
}
|
|
10147
|
+
return "failed";
|
|
10057
10148
|
}
|
|
10058
|
-
|
|
10149
|
+
);
|
|
10150
|
+
if (one) {
|
|
10151
|
+
const byOrg = one === "failed" ? /* @__PURE__ */ new Map() : new Map(one.views.map((v) => [v.organization_id, v.items]));
|
|
10152
|
+
const failed = one === "failed" ? new Set(localOrgs.map((o) => o.id)) : new Set(one.unavailable);
|
|
10153
|
+
const settled = localOrgs.map((o, i) => {
|
|
10154
|
+
const items = byOrg.get(o.id);
|
|
10155
|
+
if (failed.has(o.id) || items === void 0) return Promise.reject(new Error(`${o.name} is unavailable`));
|
|
10156
|
+
raw.orgLists[i] = { items, count: items.length };
|
|
10157
|
+
return Promise.resolve(raw.orgLists[i]);
|
|
10158
|
+
});
|
|
10159
|
+
settled.forEach(quiet);
|
|
10160
|
+
progress();
|
|
10161
|
+
return settled;
|
|
10059
10162
|
}
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
return
|
|
10163
|
+
return localOrgs.map((o, i) => {
|
|
10164
|
+
const p = retrying(() => account9.listItems(api, { query: { principal_type: "organization", organization_id: o.id } }, { organizationId: o.id }));
|
|
10165
|
+
const arrived = p.then((l) => {
|
|
10166
|
+
raw.orgLists[i] = l;
|
|
10167
|
+
progress();
|
|
10168
|
+
return l;
|
|
10169
|
+
});
|
|
10170
|
+
quiet(arrived);
|
|
10171
|
+
return arrived;
|
|
10069
10172
|
});
|
|
10070
|
-
|
|
10173
|
+
})();
|
|
10174
|
+
quiet(orgListsReady);
|
|
10175
|
+
const authDone = authP.then((a) => {
|
|
10176
|
+
localUnavailable = a.unavailable_scopes ?? [];
|
|
10177
|
+
const fresh = new Map((a.entries ?? []).map((e) => [e.credential_item_id, e]));
|
|
10178
|
+
localAuth = fresh;
|
|
10179
|
+
localState = "known";
|
|
10180
|
+
if (localUnavailable.length) unavailable.push("One-time codes");
|
|
10071
10181
|
progress();
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
const p = retrying(() => account9.listItems(api, { query: { principal_type: "organization", organization_id: o.id } }, { organizationId: o.id }));
|
|
10076
|
-
const arrived = p.then((l) => {
|
|
10077
|
-
raw.orgLists[i] = l;
|
|
10078
|
-
progress();
|
|
10079
|
-
return l;
|
|
10080
|
-
});
|
|
10081
|
-
quiet(arrived);
|
|
10082
|
-
return arrived;
|
|
10182
|
+
}, () => {
|
|
10183
|
+
localState = "unavailable";
|
|
10184
|
+
unavailable.push("One-time codes");
|
|
10083
10185
|
});
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
raw.
|
|
10186
|
+
const sharedDone = sharedP.then((sh) => {
|
|
10187
|
+
raw.shared = sh;
|
|
10188
|
+
progress();
|
|
10189
|
+
}, () => {
|
|
10190
|
+
raw.shared = { items: [], count: 0 };
|
|
10191
|
+
unavailable.push("Shared with me");
|
|
10192
|
+
});
|
|
10193
|
+
const [catalog, mine] = await Promise.all([catalogP, userP]);
|
|
10194
|
+
raw.catalog = catalog;
|
|
10195
|
+
raw.mine = mine;
|
|
10094
10196
|
progress();
|
|
10095
|
-
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10104
|
-
|
|
10105
|
-
|
|
10106
|
-
|
|
10107
|
-
|
|
10108
|
-
unavailable.push(orgs[i]?.name ?? "An organization");
|
|
10197
|
+
const orgPs = await orgListsReady;
|
|
10198
|
+
const orgSettled = await Promise.allSettled(orgPs);
|
|
10199
|
+
orgSettled.forEach((r, i) => {
|
|
10200
|
+
if (r.status === "rejected") {
|
|
10201
|
+
raw.orgLists[i] = { items: [], count: 0 };
|
|
10202
|
+
unavailable.push(localOrgs[i]?.name ?? "An organization");
|
|
10203
|
+
}
|
|
10204
|
+
});
|
|
10205
|
+
await Promise.all([authDone, sharedDone]);
|
|
10206
|
+
done = true;
|
|
10207
|
+
if (generation === loadGeneration && mutationAtStart !== authMutation) {
|
|
10208
|
+
if (restarts >= 2) throw new Error("Authenticator changed while loading. Retry");
|
|
10209
|
+
return run(restarts + 1);
|
|
10109
10210
|
}
|
|
10110
|
-
|
|
10111
|
-
|
|
10112
|
-
|
|
10113
|
-
|
|
10211
|
+
const snapshot = build();
|
|
10212
|
+
if (generation === loadGeneration) {
|
|
10213
|
+
defs = localDefs;
|
|
10214
|
+
auth = localAuth;
|
|
10215
|
+
authState = localState;
|
|
10216
|
+
authUnavailable = localUnavailable;
|
|
10217
|
+
inventoryOwners = localOwners;
|
|
10218
|
+
orgs = localOrgs;
|
|
10219
|
+
}
|
|
10220
|
+
return snapshot;
|
|
10221
|
+
};
|
|
10222
|
+
return run(0);
|
|
10114
10223
|
},
|
|
10115
10224
|
async reveal(item, fieldKey2) {
|
|
10116
10225
|
const field = item.fields.find((f) => f.key === fieldKey2);
|