@ai-matrx/vault 0.1.27 → 0.1.28
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 +8 -0
- package/dist/index.cjs +151 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +151 -21
- package/dist/index.js.map +1 -1
- package/dist/io/bitwarden-json.worker.js.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.28
|
|
4
|
+
|
|
5
|
+
- **A website typed as a bare domain always saves.** Every bare domain is sent as `https://…` on create, edit, assign and the item settings call, whether or not the server could reach it (an intranet, a site not live yet) (`normalizeLoginUrls`); the Website box shows the `https://` form after leaving it, and "Not a public website. You can still save it." is a warning, never a block. Guard: `src/review-0-1-28.test.tsx` § 1. Consumer action: none.
|
|
6
|
+
- **Every vault refusal shows the server's reason.** `createApiVaultPort` wraps the host's `Api` once (`withServerReasons`): a 4xx whose sentence is generic ("Invalid request. Please check your input and try again.") is re-worded, on the same error object, with the server's own reason ("Login URL must start with https:// or http://…"), so every action inherits it. Guard: § 1. Consumer action: none.
|
|
7
|
+
- **A restored item turns back on in one press.** Restore still comes back as the server's quarantine (item, fields, codes, browser fill and sandbox use off; shares never recreated). The restore result now offers **Turn these back on**: it turns on the item, its fields, its one-time codes and browser fill (when it has a website), then says what it turned on; sharing and sandbox use stay the person's choice (`RestorePanel turnOn`, `turnBackOn`, `turnedOnSentence`; the item's own Turn on uses the same `turnBackOn`). Not offered when the item was already off before it was deleted. Guard: § 2. Consumer action: none.
|
|
8
|
+
- **The first save of a new item leaves the form clean.** Once create resolves the pane reports clean and Save is off, even while a host's address has not moved to the saved item yet (it used to keep "Unsaved" up and ask "Discard your changes?"); `onDirty` is said when it changes, never re-said for a new callback. Guard: § 3. Consumer action: none.
|
|
9
|
+
- Guard for every row copy ending in "<label> copied" or a failure line (refused clipboard, unreadable value): § 4. Consumer action: none.
|
|
10
|
+
|
|
3
11
|
## 0.1.27
|
|
4
12
|
|
|
5
13
|
- **`emptyReadAlarm`, `holdingsDoorIsAbsent`, `HOLDINGS_DOOR_MIGRATION` and the `HoldingRow`/`Holdings` types are exported from the root** so the web app can drop its whole-file copy (`features/secrets/empty-read.ts`). Consumer action: none.
|
package/dist/index.cjs
CHANGED
|
@@ -2663,9 +2663,12 @@ function normalizeLoginUrl(input) {
|
|
|
2663
2663
|
return null;
|
|
2664
2664
|
}
|
|
2665
2665
|
}
|
|
2666
|
+
function normalizeLoginUrls(inputs) {
|
|
2667
|
+
return inputs.map((u) => u.trim()).filter(Boolean).map((u) => normalizeLoginUrl(u) ?? u);
|
|
2668
|
+
}
|
|
2666
2669
|
function describeDestination(r) {
|
|
2667
2670
|
if (r.state === "reachable") return { url: r.normalizedUrl, state: r.state, tone: "success", message: "Website confirmed" };
|
|
2668
|
-
if (r.state === "invalid") return { url: r.normalizedUrl, state: r.state, tone: "
|
|
2671
|
+
if (r.state === "invalid") return { url: r.normalizedUrl, state: r.state, tone: "warning", message: "Not a public website. You can still save it." };
|
|
2669
2672
|
if (r.state === "not_found") return { url: r.normalizedUrl, state: r.state, tone: "warning", message: "Site answered, but this page was not found" };
|
|
2670
2673
|
return { url: r.normalizedUrl, state: r.state, tone: "warning", message: "Couldn\u2019t confirm it. You can still save it." };
|
|
2671
2674
|
}
|
|
@@ -5368,6 +5371,40 @@ function AssignResult({ sent, onAnother, onDone }) {
|
|
|
5368
5371
|
] });
|
|
5369
5372
|
}
|
|
5370
5373
|
|
|
5374
|
+
// src/lists/turn-back-on.ts
|
|
5375
|
+
async function turnBackOn(port, item, fields, options = {}) {
|
|
5376
|
+
const run = options.run ?? ((op) => op());
|
|
5377
|
+
const done = [];
|
|
5378
|
+
if (item.turnedOff) {
|
|
5379
|
+
await run(() => port.fields.updateItem(item, { active: true }));
|
|
5380
|
+
done.push("the item");
|
|
5381
|
+
}
|
|
5382
|
+
let n = 0;
|
|
5383
|
+
for (const f of fields) {
|
|
5384
|
+
if (f.active) continue;
|
|
5385
|
+
await run(() => port.fields.updateField(item, f.id, { active: true }));
|
|
5386
|
+
n += 1;
|
|
5387
|
+
}
|
|
5388
|
+
if (n) done.push(n === 1 ? "1 field" : `${n} fields`);
|
|
5389
|
+
if (fields.some((f) => f.key === OTP_SEED_KEY)) {
|
|
5390
|
+
const codes = await port.authenticator.get(item);
|
|
5391
|
+
if (codes && !codes.enabled) {
|
|
5392
|
+
await run(() => port.authenticator.setEnabled(item, true));
|
|
5393
|
+
done.push("one-time codes");
|
|
5394
|
+
}
|
|
5395
|
+
}
|
|
5396
|
+
if (options.browserFill && !item.browserFillEnabled && item.loginUrls.length > 0) {
|
|
5397
|
+
await run(() => port.fields.updateItem(item, { browserFillEnabled: true }));
|
|
5398
|
+
done.push("browser fill");
|
|
5399
|
+
}
|
|
5400
|
+
return done;
|
|
5401
|
+
}
|
|
5402
|
+
function turnedOnSentence(done) {
|
|
5403
|
+
if (done.length === 0) return "Nothing here was off.";
|
|
5404
|
+
const list = done.length === 1 ? done[0] : `${done.slice(0, -1).join(", ")} and ${done[done.length - 1]}`;
|
|
5405
|
+
return `Turned on ${list}.`;
|
|
5406
|
+
}
|
|
5407
|
+
|
|
5371
5408
|
// src/ItemPane.tsx
|
|
5372
5409
|
var import_icons11 = require("@ai-matrx/design-system/icons");
|
|
5373
5410
|
var import_drafts = require("@ai-matrx/kit/drafts");
|
|
@@ -5981,7 +6018,8 @@ function ItemPane(props) {
|
|
|
5981
6018
|
const [showBrowser, setShowBrowser] = (0, import_react28.useState)(false);
|
|
5982
6019
|
const [error, setError] = (0, import_react28.useState)(null);
|
|
5983
6020
|
const [destinations, setDestinations] = (0, import_react28.useState)({});
|
|
5984
|
-
const
|
|
6021
|
+
const [savedNew, setSavedNew] = (0, import_react28.useState)(false);
|
|
6022
|
+
const dirty = editing && !savedNew && isDirty(base, draft);
|
|
5985
6023
|
const declared = definition?.fields ?? [];
|
|
5986
6024
|
const prev = (0, import_react28.useRef)(null);
|
|
5987
6025
|
const paneRef = (0, import_react28.useRef)(null);
|
|
@@ -6004,8 +6042,11 @@ function ItemPane(props) {
|
|
|
6004
6042
|
setShowTags(false);
|
|
6005
6043
|
setShowDescription(false);
|
|
6006
6044
|
setShowRecovery(false);
|
|
6045
|
+
setSavedNew(false);
|
|
6007
6046
|
}, [item?.id, mode]);
|
|
6008
|
-
(0, import_react28.
|
|
6047
|
+
const onDirtyRef = (0, import_react28.useRef)(onDirty);
|
|
6048
|
+
onDirtyRef.current = onDirty;
|
|
6049
|
+
(0, import_react28.useEffect)(() => onDirtyRef.current(dirty), [dirty]);
|
|
6009
6050
|
const keepKey = draftKey(item?.id ?? null, definition?.key ?? item?.definitionKey ?? null);
|
|
6010
6051
|
(0, import_react28.useEffect)(() => {
|
|
6011
6052
|
if (editing && dirty) (0, import_drafts.keepDraft)(keepKey, keptFieldsOf(draft), { basedOn: item?.updatedAt ?? "new" });
|
|
@@ -6062,6 +6103,8 @@ function ItemPane(props) {
|
|
|
6062
6103
|
const [withCode, n0, enrolled] = await withOtp(r.item, r.notice);
|
|
6063
6104
|
const [next, n] = await storeQueuedFiles(withCode, n0);
|
|
6064
6105
|
(0, import_drafts.dropKeptDraft)(keepKey);
|
|
6106
|
+
setSavedNew(true);
|
|
6107
|
+
onDirty(false);
|
|
6065
6108
|
onSaved(next, true, n, enrolled);
|
|
6066
6109
|
}
|
|
6067
6110
|
} catch (e) {
|
|
@@ -6124,12 +6167,7 @@ function ItemPane(props) {
|
|
|
6124
6167
|
if (!item) return;
|
|
6125
6168
|
setError(null);
|
|
6126
6169
|
try {
|
|
6127
|
-
|
|
6128
|
-
for (const f of fields) await confirmed(() => port.fields.updateField(item, f.id, { active: true }));
|
|
6129
|
-
if (fields.some((f) => f.key === OTP_SEED_KEY)) {
|
|
6130
|
-
const codes = await port.authenticator.get(item);
|
|
6131
|
-
if (codes && !codes.enabled) await confirmed(() => port.authenticator.setEnabled(item, true));
|
|
6132
|
-
}
|
|
6170
|
+
await turnBackOn(port, item, fields, { run: confirmed });
|
|
6133
6171
|
onChanged();
|
|
6134
6172
|
} catch (e) {
|
|
6135
6173
|
if (!wasCancelled(e)) setError(e instanceof Error ? e.message : "It could not be turned on.");
|
|
@@ -6184,7 +6222,7 @@ function ItemPane(props) {
|
|
|
6184
6222
|
"Unsaved"
|
|
6185
6223
|
] }) : null,
|
|
6186
6224
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "quiet", disabled: busy, onClick: () => mode === "new" ? onCancelNew() : onMode("view"), children: dirty ? "Discard" : "Cancel" }),
|
|
6187
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "primary", disabled: busy || sent !== null || draft.name.trim() === "" || mode === "edit" && !dirty || mode === "new" && owner === "person" && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(recipient.trim()), onClick: () => void save(), children: mode === "new" && owner === "person" ? "Send" : "Save" })
|
|
6225
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_controls22.Button, { variant: "primary", disabled: busy || savedNew || sent !== null || draft.name.trim() === "" || mode === "edit" && !dirty || mode === "new" && owner === "person" && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(recipient.trim()), onClick: () => void save(), children: mode === "new" && owner === "person" ? "Send" : "Save" })
|
|
6188
6226
|
] }) : item ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
6189
6227
|
host.page && canUseOnSite(item, host.page.url) ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(UseOnSiteButton, { port, item, pageUrl: host.page.url, onChanged: (next) => onChanged(next) }) : null,
|
|
6190
6228
|
host.fill && item.capabilities.canUse ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
@@ -6352,7 +6390,8 @@ function ItemPane(props) {
|
|
|
6352
6390
|
else delete next[i];
|
|
6353
6391
|
return next;
|
|
6354
6392
|
});
|
|
6355
|
-
|
|
6393
|
+
const stored = r?.url || normalizeLoginUrl(u);
|
|
6394
|
+
if (stored && stored !== u) setDraft((d) => ({ ...d, loginUrls: d.loginUrls.map((x, j) => j === i && x === u ? stored : x) }));
|
|
6356
6395
|
}),
|
|
6357
6396
|
onRemove: () => patch({ loginUrls: draft.loginUrls.filter((_, j) => j !== i) }),
|
|
6358
6397
|
removeLabel: "Remove website"
|
|
@@ -6764,8 +6803,16 @@ var import_react30 = require("react");
|
|
|
6764
6803
|
var import_controls24 = require("@ai-matrx/design-system/controls");
|
|
6765
6804
|
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
6766
6805
|
var count = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
|
|
6767
|
-
function RestorePanel({
|
|
6806
|
+
function RestorePanel({
|
|
6807
|
+
lists,
|
|
6808
|
+
item,
|
|
6809
|
+
restore,
|
|
6810
|
+
turnOn,
|
|
6811
|
+
onClose
|
|
6812
|
+
}) {
|
|
6768
6813
|
const [preview, setPreview] = (0, import_react30.useState)(null);
|
|
6814
|
+
const [restored, setRestored] = (0, import_react30.useState)(null);
|
|
6815
|
+
const [turnedOn, setTurnedOn] = (0, import_react30.useState)(null);
|
|
6769
6816
|
const [error, setError] = (0, import_react30.useState)(null);
|
|
6770
6817
|
const [busy, setBusy] = (0, import_react30.useState)(false);
|
|
6771
6818
|
const [loading, setLoading] = (0, import_react30.useState)(Boolean(lists.preview));
|
|
@@ -6794,13 +6841,54 @@ function RestorePanel({ lists, item, restore, onClose }) {
|
|
|
6794
6841
|
setError(null);
|
|
6795
6842
|
try {
|
|
6796
6843
|
const result = await restore(preview?.supported ? preview.deletionId : void 0);
|
|
6797
|
-
if (result)
|
|
6844
|
+
if (!result) return;
|
|
6845
|
+
if (turnOn && !result.alreadyRestored && preview?.supported && !preview.priorWasDisabled) setRestored(result);
|
|
6846
|
+
else onClose();
|
|
6798
6847
|
} catch (e) {
|
|
6799
6848
|
setError(e instanceof Error && e.message ? e.message : "Restore did not complete. Try again.");
|
|
6800
6849
|
} finally {
|
|
6801
6850
|
setBusy(false);
|
|
6802
6851
|
}
|
|
6803
6852
|
};
|
|
6853
|
+
const runTurnOn = async () => {
|
|
6854
|
+
if (!restored || !turnOn) return;
|
|
6855
|
+
setBusy(true);
|
|
6856
|
+
setError(null);
|
|
6857
|
+
try {
|
|
6858
|
+
const said = await turnOn(restored.item);
|
|
6859
|
+
if (said !== null) setTurnedOn(said);
|
|
6860
|
+
} catch (e) {
|
|
6861
|
+
setError(e instanceof Error && e.message ? e.message : "It could not be turned on. Turn it on from the item.");
|
|
6862
|
+
} finally {
|
|
6863
|
+
setBusy(false);
|
|
6864
|
+
}
|
|
6865
|
+
};
|
|
6866
|
+
if (restored) {
|
|
6867
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
6868
|
+
Panel,
|
|
6869
|
+
{
|
|
6870
|
+
title: `Restored "${item.name}"`,
|
|
6871
|
+
size: "md",
|
|
6872
|
+
onClose,
|
|
6873
|
+
footer: turnedOn ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_controls24.Button, { variant: "primary", onClick: onClose, children: "Done" }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
6874
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_controls24.Button, { variant: "quiet", onClick: onClose, children: "Leave off" }),
|
|
6875
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_controls24.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(RestoreGlyph, {}), disabled: busy, onClick: () => void runTurnOn(), children: busy ? "Turning on\u2026" : "Turn these back on" })
|
|
6876
|
+
] }),
|
|
6877
|
+
children: [
|
|
6878
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(VaultSection, { title: turnedOn ? "Turned on" : "Back, turned off", children: [
|
|
6879
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(VaultRow, { label: "Came back", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { className: "text-foreground", children: [
|
|
6880
|
+
count(restored.restoredFields, "field"),
|
|
6881
|
+
", ",
|
|
6882
|
+
count(restored.restoredAttachments, "file")
|
|
6883
|
+
] }) }),
|
|
6884
|
+
turnedOn ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(VaultRow, { label: "Now on", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { role: "status", className: "text-foreground", children: turnedOn }) }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(VaultRow, { label: "Turns on", hint: "One press, nothing else changes", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-muted-foreground", children: "The item, its fields, codes and browser fill" }) }),
|
|
6885
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(VaultRow, { label: "Stays off", hint: "Choose these again yourself", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-muted-foreground", children: "Sharing, sandbox use" }) })
|
|
6886
|
+
] }),
|
|
6887
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { role: "alert", className: "px-1 text-sm text-destructive", children: error }) : null
|
|
6888
|
+
]
|
|
6889
|
+
}
|
|
6890
|
+
);
|
|
6891
|
+
}
|
|
6804
6892
|
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
6805
6893
|
Panel,
|
|
6806
6894
|
{
|
|
@@ -6841,7 +6929,7 @@ function TrashView({ port, deleted, query, header, onChanged }) {
|
|
|
6841
6929
|
setMessage(null);
|
|
6842
6930
|
try {
|
|
6843
6931
|
const r = await confirmed(() => port.lists.restore(item, deletionId));
|
|
6844
|
-
setMessage({ tone: "ok", text: r.alreadyRestored ? `"${item.name}" was already restored.` : `Restored "${item.name}" with ${r.restoredFields} fields and ${r.restoredAttachments} files
|
|
6932
|
+
setMessage({ tone: "ok", text: r.alreadyRestored ? `"${item.name}" was already restored.` : `Restored "${item.name}" with ${r.restoredFields} fields and ${r.restoredAttachments} files, turned off.` });
|
|
6845
6933
|
onChanged();
|
|
6846
6934
|
return r;
|
|
6847
6935
|
} catch (e) {
|
|
@@ -6851,6 +6939,19 @@ function TrashView({ port, deleted, query, header, onChanged }) {
|
|
|
6851
6939
|
setBusyId(null);
|
|
6852
6940
|
}
|
|
6853
6941
|
};
|
|
6942
|
+
const turnOn = async (item) => {
|
|
6943
|
+
try {
|
|
6944
|
+
const done = await turnBackOn(port, item, item.fields, { browserFill: true, run: confirmed });
|
|
6945
|
+
const said = turnedOnSentence(done);
|
|
6946
|
+
setMessage({ tone: "ok", text: `"${item.name}": ${said}` });
|
|
6947
|
+
onChanged();
|
|
6948
|
+
return said;
|
|
6949
|
+
} catch (e) {
|
|
6950
|
+
onChanged();
|
|
6951
|
+
if (wasCancelled(e)) return null;
|
|
6952
|
+
throw e;
|
|
6953
|
+
}
|
|
6954
|
+
};
|
|
6854
6955
|
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto p-3", children: [
|
|
6855
6956
|
header,
|
|
6856
6957
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(VaultSection, { title: "Trash", children: shown.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Empty, { children: deleted.length === 0 ? "The trash is empty." : "Nothing matches." }) : shown.map((item) => (
|
|
@@ -6864,7 +6965,7 @@ function TrashView({ port, deleted, query, header, onChanged }) {
|
|
|
6864
6965
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_controls25.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(RestoreGlyph, {}), disabled: busyId !== null || !item.deletion, onClick: () => setRestoring(item), children: "Restore" })
|
|
6865
6966
|
] }, item.id)
|
|
6866
6967
|
)) }),
|
|
6867
|
-
restoring ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(RestorePanel, { lists: port.lists, item: restoring, restore: (deletionId) => restore(restoring, deletionId), onClose: () => setRestoring(null) }) : null,
|
|
6968
|
+
restoring ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(RestorePanel, { lists: port.lists, item: restoring, restore: (deletionId) => restore(restoring, deletionId), turnOn, onClose: () => setRestoring(null) }) : null,
|
|
6868
6969
|
message2 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { role: "status", className: message2.tone === "error" ? "px-1 text-sm text-destructive" : "px-1 text-sm text-muted-foreground", children: message2.text }) : null
|
|
6869
6970
|
] });
|
|
6870
6971
|
}
|
|
@@ -9129,7 +9230,7 @@ function createFieldsPort(ctx) {
|
|
|
9129
9230
|
const body = { initiation: "user" };
|
|
9130
9231
|
if (patch.uriMatchMode !== void 0) body.uri_match_mode = patch.uriMatchMode;
|
|
9131
9232
|
if (patch.browserFillEnabled !== void 0) body.browser_fill_enabled = patch.browserFillEnabled;
|
|
9132
|
-
if (patch.loginUrls !== void 0) body.login_urls = patch.loginUrls;
|
|
9233
|
+
if (patch.loginUrls !== void 0) body.login_urls = normalizeLoginUrls(patch.loginUrls);
|
|
9133
9234
|
if (patch.active !== void 0) body.status = patch.active ? "active" : "disabled";
|
|
9134
9235
|
await account3.updateItem(api, { path: { item_id: item.id }, body }, ctx.optsFor(item));
|
|
9135
9236
|
return reread(item);
|
|
@@ -9141,6 +9242,33 @@ function createFieldsPort(ctx) {
|
|
|
9141
9242
|
};
|
|
9142
9243
|
}
|
|
9143
9244
|
|
|
9245
|
+
// src/server-reasons.ts
|
|
9246
|
+
var GENERIC = [/please check your input/i, /^\s*request failed\s*\(\d+\)\s*$/i, /^\s*bad request\.?\s*$/i, /^\s*HTTP \d+\s*$/i];
|
|
9247
|
+
var isGeneric = (s) => GENERIC.some((re) => re.test(s));
|
|
9248
|
+
function cleanReason(detail) {
|
|
9249
|
+
const s = detail.replace(/^\s*validation error:\s*/i, "").replace(/^\s*value error,\s*/i, "").trim();
|
|
9250
|
+
return s ? s.charAt(0).toUpperCase() + s.slice(1) : s;
|
|
9251
|
+
}
|
|
9252
|
+
function serverReason(e) {
|
|
9253
|
+
if (!e || typeof e !== "object") return null;
|
|
9254
|
+
const { status, detail, message: message2 } = e;
|
|
9255
|
+
if (typeof status !== "number" || status < 400 || status >= 500) return null;
|
|
9256
|
+
if (typeof detail !== "string" || !detail.trim() || isGeneric(detail)) return null;
|
|
9257
|
+
const shown = typeof message2 === "string" ? message2 : "";
|
|
9258
|
+
if (shown && !isGeneric(shown)) return null;
|
|
9259
|
+
return cleanReason(detail);
|
|
9260
|
+
}
|
|
9261
|
+
function withReason(e) {
|
|
9262
|
+
const reason = serverReason(e);
|
|
9263
|
+
if (reason) Object.assign(e, { message: reason, userMessage: reason });
|
|
9264
|
+
return e;
|
|
9265
|
+
}
|
|
9266
|
+
function withServerReasons(api) {
|
|
9267
|
+
const wrapped = Object.create(api);
|
|
9268
|
+
wrapped.call = (...args) => api.call(...args).catch((e) => Promise.reject(withReason(e)));
|
|
9269
|
+
return wrapped;
|
|
9270
|
+
}
|
|
9271
|
+
|
|
9144
9272
|
// src/io/api.ts
|
|
9145
9273
|
var account4 = __toESM(require("@ai-matrx/api/account"), 1);
|
|
9146
9274
|
var orgOpts = (organizationId) => organizationId ? { organizationId } : void 0;
|
|
@@ -9413,7 +9541,7 @@ function createSharingPort(ctx, options = {}) {
|
|
|
9413
9541
|
definition_key: definition.key,
|
|
9414
9542
|
provider_key: definition.providerKey,
|
|
9415
9543
|
tags: draft.tags,
|
|
9416
|
-
login_urls: draft.loginUrls,
|
|
9544
|
+
login_urls: normalizeLoginUrls(draft.loginUrls),
|
|
9417
9545
|
notes: draft.notes || null,
|
|
9418
9546
|
generate_field_key: generateFieldKey,
|
|
9419
9547
|
initiation: "user",
|
|
@@ -9610,7 +9738,8 @@ async function retrying(run, tries = 3) {
|
|
|
9610
9738
|
}
|
|
9611
9739
|
}
|
|
9612
9740
|
}
|
|
9613
|
-
function createApiVaultPort(
|
|
9741
|
+
function createApiVaultPort(hostApi, options = {}) {
|
|
9742
|
+
const api = withServerReasons(hostApi);
|
|
9614
9743
|
let defs = /* @__PURE__ */ new Map();
|
|
9615
9744
|
let auth = /* @__PURE__ */ new Map();
|
|
9616
9745
|
let orgs = [];
|
|
@@ -9765,6 +9894,7 @@ function createApiVaultPort(api, options = {}) {
|
|
|
9765
9894
|
const declared = definition.fields;
|
|
9766
9895
|
const encrypted = declared.filter((f) => f.storage === "encrypted" && (draft.values[f.key] ?? "") !== "");
|
|
9767
9896
|
const meta = declared.filter((f) => f.storage === "metadata" && (draft.values[f.key] ?? "") !== "");
|
|
9897
|
+
const loginUrls = normalizeLoginUrls(draft.loginUrls);
|
|
9768
9898
|
const w = await account9.createItem(api, {
|
|
9769
9899
|
body: {
|
|
9770
9900
|
display_name: draft.name.trim(),
|
|
@@ -9772,10 +9902,10 @@ function createApiVaultPort(api, options = {}) {
|
|
|
9772
9902
|
definition_key: definition.key,
|
|
9773
9903
|
provider_key: definition.providerKey,
|
|
9774
9904
|
tags: draft.tags,
|
|
9775
|
-
login_urls:
|
|
9905
|
+
login_urls: loginUrls,
|
|
9776
9906
|
// A login a person saves with its website is offered on that website (the server's own
|
|
9777
9907
|
// default is off, which made "Save a login" on a page answer "No saved login" there).
|
|
9778
|
-
...draft.browser ? { uri_match_mode: draft.browser.uriMatchMode, browser_fill_enabled: draft.browser.browserFillEnabled } :
|
|
9908
|
+
...draft.browser ? { uri_match_mode: draft.browser.uriMatchMode, browser_fill_enabled: draft.browser.browserFillEnabled } : loginUrls.length > 0 ? { browser_fill_enabled: true } : {},
|
|
9779
9909
|
notes: draft.notes || null,
|
|
9780
9910
|
principal: organizationId ? { type: "organization", organization_id: organizationId } : { type: "user" },
|
|
9781
9911
|
initiation: "user",
|
|
@@ -9802,7 +9932,7 @@ function createApiVaultPort(api, options = {}) {
|
|
|
9802
9932
|
else body.description = draft.description.trim();
|
|
9803
9933
|
}
|
|
9804
9934
|
if (JSON.stringify(draft.tags) !== JSON.stringify(item.tags)) body.tags = draft.tags;
|
|
9805
|
-
if (JSON.stringify(draft.loginUrls) !== JSON.stringify(item.loginUrls)) body.login_urls = draft.loginUrls;
|
|
9935
|
+
if (JSON.stringify(draft.loginUrls) !== JSON.stringify(item.loginUrls)) body.login_urls = normalizeLoginUrls(draft.loginUrls);
|
|
9806
9936
|
if (draft.notes !== item.notes) {
|
|
9807
9937
|
if (draft.notes === "") body.clear_notes = true;
|
|
9808
9938
|
else body.notes = draft.notes;
|