@gjermundgaraba/upup 0.5.0 → 0.5.2
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/README.md +3 -1
- package/dist/{Tui-DV_6wbrQ.mjs → Tui-Ri-yz-Sx.mjs} +58 -25
- package/dist/grep-worker.mjs +3 -16
- package/dist/{io-ns6xhFdF.mjs → io-Dgi-IiVu.mjs} +11 -1
- package/dist/main.mjs +501 -525
- package/native/dist/upup-sparkle.json +1 -1
- package/native/dist/upup-sparkle.zip +0 -0
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -107,6 +107,8 @@ Agents can't choose the audit model or add credentials; set those in the TUI. Wi
|
|
|
107
107
|
| `q` | Quit, after stopping a running update |
|
|
108
108
|
| Ctrl+C | Quit immediately |
|
|
109
109
|
|
|
110
|
+
Text you mark with the mouse, such as an error message, is copied to the clipboard as you let go, and the status line says so.
|
|
111
|
+
|
|
110
112
|
Details have four tabs. **About** shows what the package is: its provider, versions and declaration, plus a description, release dates, popularity, deprecations and links from its registry, cached for a day under your user cache directory. **Changes** shows the verified diff between the installed and offered versions without needing a model; for an app update, the release notes from the app's feed. **Report** and **Transcript** show the latest Audit's verdict and what the model did.
|
|
111
113
|
|
|
112
114
|
When npm's allow-scripts policy blocks an update, upup asks before retrying: it names the blocked install scripts. For a global install, approving adds those packages to your allow list for that one command. For a project, npm accepts no allow list from the command line, so approving turns its strict check off for that one install, and any install script in the project's dependencies may run. If npm's strict check crashes instead (`ENOENT` on a `lib` folder, which happens with `vp add -g` and `strict-allow-scripts=true`), retrying turns the strict check off for that one command, and the popup says so. Saying no skips the update, unless upup was already installing a project's edits: those are written to its lockfile by then, so the update fails, saying npm blocked its install scripts.
|
|
@@ -130,6 +132,6 @@ vp pack
|
|
|
130
132
|
|
|
131
133
|
`vp run sparkle` builds upup-sparkle (`native/sparkle`) on macOS, with Xcode's command line tools, into `native/dist`, signed ad hoc unless `UPUP_SIGNING_IDENTITY` names a signing identity, and notarized when `UPUP_NOTARY_KEY` (an App Store Connect API key's .p8 file), `UPUP_NOTARY_KEY_ID` and `UPUP_NOTARY_ISSUER` are set; and then starts it as upup would; the release workflow signs and notarizes it with a Developer ID, and starts it only once its signing identity and notary key are gone. `vp run sparkle:e2e` builds it and installs updates of a fixture app through it, from an appcast served on this Mac.
|
|
132
134
|
|
|
133
|
-
`vp run
|
|
135
|
+
`vp run install:local` installs this checkout as a global package, as the release publishes it: on macOS it builds upup-sparkle signed with the `Apple Development` identity in your keychain, or the one `UPUP_SIGNING_IDENTITY` names, notarized only when the notary variables above are set, and starts it as upup would; then it builds the package, packs it and installs the tarball with `vp install -g`. A helper signed by any team updates Sparkle apps that ship installer packages, which one signed ad hoc can't.
|
|
134
136
|
|
|
135
137
|
The Go implementation lives in history at `d2a5ed7`.
|
|
@@ -3,8 +3,8 @@ import { Deferred, Effect, FiberSet, Stream } from "effect";
|
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import semver from "semver";
|
|
6
|
-
import { createComponent, createElement, createTextNode, effect, insert, insertNode, memo, onAnimationTick, render, setProp, use, useKeyboard, usePaste, useTerminalDimensions } from "@opentui/solid";
|
|
7
|
-
import { TextAttributes, createCliRenderer } from "@opentui/core";
|
|
6
|
+
import { createComponent, createElement, createTextNode, effect, insert, insertNode, memo, onAnimationTick, render, setProp, use, useKeyboard, usePaste, useSelectionHandler, useTerminalDimensions } from "@opentui/solid";
|
|
7
|
+
import { TextAttributes, createCliRenderer, createClipboard, createHostClipboard, createRendererClipboardAdapter } from "@opentui/core";
|
|
8
8
|
import { For, Match, Show, Switch, createEffect, createMemo, createRenderEffect, createSignal, on, onCleanup, untrack } from "solid-js/dist/solid.js";
|
|
9
9
|
// src/tui/text.ts
|
|
10
10
|
const segmenter = new Intl.Segmenter();
|
|
@@ -1493,7 +1493,7 @@ const verdictIcons = {
|
|
|
1493
1493
|
danger: "✗",
|
|
1494
1494
|
"no-verdict": "?"
|
|
1495
1495
|
};
|
|
1496
|
-
const
|
|
1496
|
+
const ranTags = /* @__PURE__ */ new Set([
|
|
1497
1497
|
"Completed",
|
|
1498
1498
|
"Failed",
|
|
1499
1499
|
"Unexpected"
|
|
@@ -1506,12 +1506,13 @@ const status = (state, row) => {
|
|
|
1506
1506
|
busy: true
|
|
1507
1507
|
};
|
|
1508
1508
|
const audit = auditOf(state, row.pkg);
|
|
1509
|
-
const
|
|
1509
|
+
const last = state.ran[row.key];
|
|
1510
|
+
const outcome = last?.outcome;
|
|
1510
1511
|
if (state.removed[row.key] !== void 0) return {
|
|
1511
1512
|
text: "– removed",
|
|
1512
1513
|
color: theme.muted
|
|
1513
1514
|
};
|
|
1514
|
-
if (outcome !== void 0 && (audit === void 0 ||
|
|
1515
|
+
if (outcome !== void 0 && (audit === void 0 || ranTags.has(outcome._tag))) {
|
|
1515
1516
|
if (outcome._tag === "Completed" && auditedAs(state, row.pkg) !== row.pkg) {
|
|
1516
1517
|
const verdict = audit?._tag === "Done" && audit.result._tag !== "Failed" ? audit.result.report.verdict : void 0;
|
|
1517
1518
|
return verdict === void 0 ? {
|
|
@@ -1522,7 +1523,7 @@ const status = (state, row) => {
|
|
|
1522
1523
|
color: verdictColor(verdict)
|
|
1523
1524
|
};
|
|
1524
1525
|
}
|
|
1525
|
-
const unremoved =
|
|
1526
|
+
const unremoved = last?.action === "uninstall" && outcome._tag === "Failed";
|
|
1526
1527
|
return {
|
|
1527
1528
|
text: `${outcomeIcon(outcome)} ${unremoved ? "not removed" : outcomeLabel(outcome)}`,
|
|
1528
1529
|
color: outcomeColor(outcome)
|
|
@@ -1590,7 +1591,7 @@ const flaggedFirst = (pending) => {
|
|
|
1590
1591
|
const flagged = new Set(pending.flagged.map((entry) => entry.row.key));
|
|
1591
1592
|
return [...pending.rows.filter((row) => flagged.has(row.key)), ...pending.rows.filter((row) => !flagged.has(row.key))];
|
|
1592
1593
|
};
|
|
1593
|
-
const markOf = (pending, row) => {
|
|
1594
|
+
const markOf = (pending, row, auditFirst) => {
|
|
1594
1595
|
const verdict = pending.flagged.find((entry) => entry.row.key === row.key)?.verdict;
|
|
1595
1596
|
const mixed = pending.unaudited.length > 0 && pending.unaudited.length < pending.rows.length;
|
|
1596
1597
|
if (verdict === void 0 && pending.unauditable.includes(row.key)) {
|
|
@@ -1605,7 +1606,7 @@ const markOf = (pending, row) => {
|
|
|
1605
1606
|
text: `${verdictIcons.ok} ${verdictLabels.ok}`,
|
|
1606
1607
|
color: theme.ok
|
|
1607
1608
|
};
|
|
1608
|
-
const unread = pending.action === "update" && pending.unaudited.includes(row.key) && (mixed || !
|
|
1609
|
+
const unread = pending.action === "update" && pending.unaudited.includes(row.key) && (mixed || !auditFirst);
|
|
1609
1610
|
if (pending.action === "update" && pending.breaking.includes(row.key)) return {
|
|
1610
1611
|
text: `${breakingMark}${unread ? " · not audited" : ""}`,
|
|
1611
1612
|
color: theme.warn
|
|
@@ -1673,7 +1674,7 @@ const ConfirmPopup = (props) => {
|
|
|
1673
1674
|
const version = (row) => removing() ? row.pkg.current : `→ ${row.pkg.target ?? ""}`;
|
|
1674
1675
|
const layout = createMemo(() => {
|
|
1675
1676
|
const rows = flaggedFirst(pending());
|
|
1676
|
-
const marks = rows.map((row) => markOf(pending(), row));
|
|
1677
|
+
const marks = rows.map((row) => markOf(pending(), row, props.state.preferences.auditFirst));
|
|
1677
1678
|
const versions = Math.min(16, widest$1(rows.map(version)));
|
|
1678
1679
|
const verdicts = widest$1(marks.map((mark) => mark?.text ?? ""));
|
|
1679
1680
|
const longest = widest$1(rows.map((row) => row.pkg.name));
|
|
@@ -2803,7 +2804,7 @@ const tabTitles = {
|
|
|
2803
2804
|
report: "Report",
|
|
2804
2805
|
transcript: "Transcript"
|
|
2805
2806
|
};
|
|
2806
|
-
const storedTranscript = (audit) => audit?._tag === "Done" && audit.result._tag === "Reported" ? audit.result
|
|
2807
|
+
const storedTranscript = (audit) => audit?._tag === "Done" && audit.result._tag === "Reported" ? audit.result : void 0;
|
|
2807
2808
|
const liveTranscript = (audit) => {
|
|
2808
2809
|
if (audit?._tag === "Running") return audit.transcript;
|
|
2809
2810
|
if (audit?._tag === "Done" && audit.result._tag === "Failed") return audit.result.transcript;
|
|
@@ -2829,13 +2830,15 @@ const Details = (props) => (() => {
|
|
|
2829
2830
|
const pkg = () => row().pkg;
|
|
2830
2831
|
const reviewed = () => auditedAs(props.state, pkg());
|
|
2831
2832
|
const notices = () => props.state.providers.flatMap((entry) => entry.notices).filter((notice) => notice.rowKey === row().key);
|
|
2832
|
-
const
|
|
2833
|
+
const ran = () => props.state.ran[row().key];
|
|
2834
|
+
const outcome = () => ran()?.outcome;
|
|
2835
|
+
const removal = () => ran()?.action === "uninstall";
|
|
2833
2836
|
const gone = () => props.state.removed[row().key] !== void 0;
|
|
2834
2837
|
const nothing = () => gone() ? "it was removed" : unauditable(reviewed());
|
|
2835
2838
|
const failure = () => {
|
|
2836
2839
|
const latest = outcome();
|
|
2837
2840
|
if (latest?._tag !== "Failed" && latest?._tag !== "Unexpected") return;
|
|
2838
|
-
const what =
|
|
2841
|
+
const what = removal() ? "Removal" : "Update";
|
|
2839
2842
|
const told = describeOutcome(latest);
|
|
2840
2843
|
const whole = `✗ ${what} ${told.charAt(0).toLowerCase()}${told.slice(1)}`;
|
|
2841
2844
|
const room = props.width - 4;
|
|
@@ -2849,7 +2852,7 @@ const Details = (props) => (() => {
|
|
|
2849
2852
|
return `Updated from ${reviewed().current} · audited before installing`;
|
|
2850
2853
|
};
|
|
2851
2854
|
const updateLabel = (latest) => {
|
|
2852
|
-
if (
|
|
2855
|
+
if (removal()) return "Removal";
|
|
2853
2856
|
if (latest._tag === "Completed") return "Updated";
|
|
2854
2857
|
return "Update";
|
|
2855
2858
|
};
|
|
@@ -3172,8 +3175,8 @@ const Details = (props) => (() => {
|
|
|
3172
3175
|
return auditOf(props.state, pkg())?._tag === "Running";
|
|
3173
3176
|
},
|
|
3174
3177
|
get stored() {
|
|
3175
|
-
const
|
|
3176
|
-
return
|
|
3178
|
+
const stored = storedTranscript(auditOf(props.state, pkg()));
|
|
3179
|
+
return stored === void 0 ? void 0 : props.state.transcripts[stored.file];
|
|
3177
3180
|
},
|
|
3178
3181
|
get unauditable() {
|
|
3179
3182
|
return nothing();
|
|
@@ -3429,7 +3432,7 @@ const PackageLine = (props) => {
|
|
|
3429
3432
|
const { target } = pkg();
|
|
3430
3433
|
return target === void 0 ? void 0 : bump(pkg().current, target, pkg().ecosystem);
|
|
3431
3434
|
};
|
|
3432
|
-
const outcome = () => props.state.
|
|
3435
|
+
const outcome = () => props.state.ran[props.row.key]?.outcome;
|
|
3433
3436
|
const marker = () => {
|
|
3434
3437
|
if (selected()) return {
|
|
3435
3438
|
text: "●",
|
|
@@ -4609,6 +4612,19 @@ const App = (props) => {
|
|
|
4609
4612
|
onCleanup(() => {
|
|
4610
4613
|
clearTimeout(toastTimer);
|
|
4611
4614
|
});
|
|
4615
|
+
useSelectionHandler((selection) => {
|
|
4616
|
+
const text = selection.getSelectedText();
|
|
4617
|
+
if (text.length === 0) return;
|
|
4618
|
+
props.run(props.clipboard.copy(text).pipe(Effect.flatMap((copied) => Effect.sync(() => {
|
|
4619
|
+
showToast(copied ? {
|
|
4620
|
+
color: theme.accent,
|
|
4621
|
+
text: "Copied to the clipboard"
|
|
4622
|
+
} : {
|
|
4623
|
+
color: theme.danger,
|
|
4624
|
+
text: "✗ Could not copy to the clipboard"
|
|
4625
|
+
});
|
|
4626
|
+
}))));
|
|
4627
|
+
});
|
|
4612
4628
|
let scroller;
|
|
4613
4629
|
const loadedOnce = createMemo((was) => !props.state.discovering && (was || !props.state.providers.some((entry) => entry.status === "loading")), false);
|
|
4614
4630
|
const [fold, setFold] = createSignal("auto");
|
|
@@ -4617,7 +4633,7 @@ const App = (props) => {
|
|
|
4617
4633
|
const updatesToDecideOn = !loadedOnce() || found.some((group) => group.kind === "updates");
|
|
4618
4634
|
const foldsUntilToggled = filter() === "" && updatesToDecideOn;
|
|
4619
4635
|
const folds = fold() === "auto" ? foldsUntilToggled : fold() === "hidden";
|
|
4620
|
-
const kept = (row) => props.state.
|
|
4636
|
+
const kept = (row) => props.state.ran[row.key] !== void 0 || props.state.selected.includes(row.key);
|
|
4621
4637
|
return found.map((group) => group.kind === "current" && folds ? {
|
|
4622
4638
|
...group,
|
|
4623
4639
|
collapsed: true,
|
|
@@ -4771,7 +4787,7 @@ const App = (props) => {
|
|
|
4771
4787
|
const transcript = createMemo(() => {
|
|
4772
4788
|
const row = shown("transcript");
|
|
4773
4789
|
return row === void 0 ? void 0 : storedTranscript(auditOf(props.state, row.pkg));
|
|
4774
|
-
});
|
|
4790
|
+
}, void 0, { equals: (previous, next) => previous?.file === next?.file });
|
|
4775
4791
|
createEffect(on(described, (key) => {
|
|
4776
4792
|
const row = untrack(focused);
|
|
4777
4793
|
if (key !== void 0 && row !== void 0) fire(props.session.describe(row.pkg));
|
|
@@ -4799,15 +4815,15 @@ const App = (props) => {
|
|
|
4799
4815
|
if (now === "transcript" || scroller === void 0) return;
|
|
4800
4816
|
scroller.scrollTo(readTo.get(now) ?? 0);
|
|
4801
4817
|
}));
|
|
4802
|
-
createEffect(on(transcript, (
|
|
4803
|
-
if (
|
|
4818
|
+
createEffect(on(transcript, (stored) => {
|
|
4819
|
+
if (stored !== void 0) fire(props.session.readTranscript(stored));
|
|
4804
4820
|
}));
|
|
4805
4821
|
createEffect(on(operating, (now, before) => {
|
|
4806
4822
|
if (before !== true || now) return;
|
|
4807
4823
|
const last = untrack(run);
|
|
4808
4824
|
if (last === void 0) return;
|
|
4809
4825
|
const outcomes = last.targets.flatMap((target) => {
|
|
4810
|
-
const outcome = props.state.
|
|
4826
|
+
const outcome = props.state.ran[target.key]?.outcome;
|
|
4811
4827
|
return outcome === void 0 ? [] : [outcome];
|
|
4812
4828
|
});
|
|
4813
4829
|
if (outcomes.length === 0) return;
|
|
@@ -4898,7 +4914,6 @@ const App = (props) => {
|
|
|
4898
4914
|
action,
|
|
4899
4915
|
rows: actionable,
|
|
4900
4916
|
flagged,
|
|
4901
|
-
auditFirst: props.state.preferences.auditFirst,
|
|
4902
4917
|
breaking: action === "update" ? actionable.filter((row) => majorsOf(props.state, row.pkg).length > 0).map((row) => row.key) : [],
|
|
4903
4918
|
unaudited: action === "update" ? actionable.filter((row) => !isUnauditedUpdate(row.pkg) && auditOf(props.state, row.pkg)?._tag !== "Done").map((row) => row.key) : [],
|
|
4904
4919
|
unauditable: actionable.filter((row) => isUnauditedUpdate(row.pkg)).map((row) => row.key),
|
|
@@ -5100,7 +5115,7 @@ const App = (props) => {
|
|
|
5100
5115
|
};
|
|
5101
5116
|
const progress = () => {
|
|
5102
5117
|
const targets = run()?.targets ?? [];
|
|
5103
|
-
const done = targets.filter((target) => props.state.
|
|
5118
|
+
const done = targets.filter((target) => props.state.ran[target.key] !== void 0).length;
|
|
5104
5119
|
const total = Math.max(targets.length, done + Object.keys(props.state.activity).length);
|
|
5105
5120
|
return `${run()?.action === "uninstall" ? "Removing" : "Updating"} ${done} of ${total}`;
|
|
5106
5121
|
};
|
|
@@ -5747,8 +5762,25 @@ const App = (props) => {
|
|
|
5747
5762
|
return _el$;
|
|
5748
5763
|
})();
|
|
5749
5764
|
};
|
|
5765
|
+
// src/tui/clipboard.ts
|
|
5766
|
+
const took = ({ host, terminal }) => host.status === "written" || terminal.status === "attempted" && terminal.capability !== "unsupported";
|
|
5767
|
+
/**
|
|
5768
|
+
* The clipboard of the machine the terminal shows: the system pasteboard
|
|
5769
|
+
* when the terminal is local, falling back to OSC 52, and OSC 52 when it is
|
|
5770
|
+
* remote. OSC 52 can't say whether the terminal took the text, so a terminal
|
|
5771
|
+
* that doesn't say it lacks OSC 52 counts as having taken it.
|
|
5772
|
+
*/
|
|
5773
|
+
const systemClipboard = Effect.fn("Tui.systemClipboard")(function* (renderer) {
|
|
5774
|
+
const service = yield* Effect.acquireRelease(Effect.sync(() => createClipboard({
|
|
5775
|
+
host: createHostClipboard(),
|
|
5776
|
+
terminal: createRendererClipboardAdapter(renderer)
|
|
5777
|
+
})), (created) => Effect.promise(async () => {
|
|
5778
|
+
await created.dispose();
|
|
5779
|
+
}));
|
|
5780
|
+
return { copy: (text) => Effect.tryPromise(async () => await service.writeText(text, { destination: "best-available" })).pipe(Effect.map(took), Effect.orElseSucceed(() => false)) };
|
|
5781
|
+
});
|
|
5750
5782
|
// src/tui/Tui.tsx
|
|
5751
|
-
const connect = Effect.fn("Tui.connect")(function* (projectDir) {
|
|
5783
|
+
const connect = Effect.fn("Tui.connect")(function* (projectDir, clipboard) {
|
|
5752
5784
|
const session = yield* Session;
|
|
5753
5785
|
const run = yield* FiberSet.makeRuntime();
|
|
5754
5786
|
const [state, setState] = createSignal(yield* session.state);
|
|
@@ -5766,6 +5798,7 @@ const connect = Effect.fn("Tui.connect")(function* (projectDir) {
|
|
|
5766
5798
|
run(effect);
|
|
5767
5799
|
},
|
|
5768
5800
|
projectDir,
|
|
5801
|
+
clipboard,
|
|
5769
5802
|
quit
|
|
5770
5803
|
});
|
|
5771
5804
|
return {
|
|
@@ -5775,10 +5808,10 @@ const connect = Effect.fn("Tui.connect")(function* (projectDir) {
|
|
|
5775
5808
|
};
|
|
5776
5809
|
});
|
|
5777
5810
|
const launch = Effect.fn("Tui.launch")(function* (projectDir) {
|
|
5778
|
-
const { view, quit, done } = yield* connect(projectDir);
|
|
5779
5811
|
const renderer = yield* Effect.acquireRelease(Effect.promise(async () => await createCliRenderer({ exitOnCtrlC: true })), (created) => Effect.sync(() => {
|
|
5780
5812
|
created.destroy();
|
|
5781
5813
|
}));
|
|
5814
|
+
const { view, quit, done } = yield* connect(projectDir, yield* systemClipboard(renderer));
|
|
5782
5815
|
renderer.on("destroy", quit);
|
|
5783
5816
|
const adopt = Effect.promise(async () => {
|
|
5784
5817
|
const colors = await renderer.getPalette({ timeout: 300 }).catch(() => void 0);
|
package/dist/grep-worker.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as
|
|
1
|
+
import { i as reason, n as decodeUtf8, r as openFlags, t as byName } from "./io-Dgi-IiVu.mjs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { closeSync, fstatSync, openSync, readSync, readdirSync } from "node:fs";
|
|
4
4
|
import { parentPort } from "node:worker_threads";
|
|
@@ -10,16 +10,6 @@ var GrepError = class extends Error {
|
|
|
10
10
|
}
|
|
11
11
|
};
|
|
12
12
|
const tooLarge = "grep result exceeds 256 KiB; narrow the path or pattern";
|
|
13
|
-
const decode = (bytes) => {
|
|
14
|
-
try {
|
|
15
|
-
return new TextDecoder("utf-8", {
|
|
16
|
-
fatal: true,
|
|
17
|
-
ignoreBOM: true
|
|
18
|
-
}).decode(bytes);
|
|
19
|
-
} catch {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
13
|
const utf8Length = (text, from, to) => {
|
|
24
14
|
let bytes = 0;
|
|
25
15
|
for (let i = from; i < to; i += 1) {
|
|
@@ -119,7 +109,7 @@ const scan = (job, every, fd, name, used) => {
|
|
|
119
109
|
continue;
|
|
120
110
|
}
|
|
121
111
|
if (line.length > 0 && line.at(-1) === 13) line = line.subarray(0, -1);
|
|
122
|
-
const text = line.includes(0) ? void 0 :
|
|
112
|
+
const text = line.includes(0) ? void 0 : decodeUtf8(line);
|
|
123
113
|
if (text === void 0) return { notText: true };
|
|
124
114
|
const before = out.length;
|
|
125
115
|
grepLine(job, every, out, name, number, lineAt, line, text);
|
|
@@ -176,10 +166,7 @@ const runGrep = (job) => {
|
|
|
176
166
|
add(`skipped: open ${name}: ${reason(error)}\n`);
|
|
177
167
|
return;
|
|
178
168
|
}
|
|
179
|
-
entries.sort(
|
|
180
|
-
if (left.name < right.name) return -1;
|
|
181
|
-
return left.name > right.name ? 1 : 0;
|
|
182
|
-
});
|
|
169
|
+
entries.sort(byName);
|
|
183
170
|
for (const entry of entries) {
|
|
184
171
|
const child = name === "." ? entry.name : `${name}/${entry.name}`;
|
|
185
172
|
const absoluteChild = path.join(absolute, entry.name);
|
|
@@ -15,9 +15,19 @@ const reason = (cause) => {
|
|
|
15
15
|
const code = cause instanceof Error && "code" in cause && Predicate.isString(cause.code) ? cause.code : void 0;
|
|
16
16
|
return code === void 0 ? "I/O error" : reasons.get(code) ?? code;
|
|
17
17
|
};
|
|
18
|
+
const decodeUtf8 = (bytes) => {
|
|
19
|
+
try {
|
|
20
|
+
return new TextDecoder("utf-8", {
|
|
21
|
+
fatal: true,
|
|
22
|
+
ignoreBOM: true
|
|
23
|
+
}).decode(bytes);
|
|
24
|
+
} catch {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
18
28
|
const byName = (left, right) => {
|
|
19
29
|
if (left.name < right.name) return -1;
|
|
20
30
|
return left.name > right.name ? 1 : 0;
|
|
21
31
|
};
|
|
22
32
|
const openFlags = constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOFOLLOW;
|
|
23
|
-
export {
|
|
33
|
+
export { reason as i, decodeUtf8 as n, openFlags as r, byName as t };
|