mintree 0.4.2 → 0.4.4
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/dist/commands/dashboard.js +53 -11
- package/dist/commands/update.d.ts +10 -0
- package/dist/commands/update.js +64 -0
- package/dist/lib/update.d.ts +16 -0
- package/dist/lib/update.js +43 -0
- package/package.json +1 -1
|
@@ -23,11 +23,19 @@ export const description = "Interactive dashboard listing open issues assigned t
|
|
|
23
23
|
function isOrphan(d) {
|
|
24
24
|
return d.orphan === true;
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// Matches an issue against the live numeric filter by substring on the digit
|
|
27
|
+
// portion of its id ("PLA-234" → "234", "BE-34" → "34"). Letters are ignored,
|
|
28
|
+
// so the user filters by ticket number alone. Empty filter matches everything.
|
|
29
|
+
function issueMatchesFilter(d, filter) {
|
|
30
|
+
if (!filter)
|
|
31
|
+
return true;
|
|
32
|
+
return d.issue.id.replace(/\D/g, "").includes(filter);
|
|
33
|
+
}
|
|
34
|
+
function tabIssues(issues, tab, filter = "") {
|
|
35
|
+
return issues.filter((d) => (tab === "issues" ? !isOrphan(d) : isOrphan(d)) && issueMatchesFilter(d, filter));
|
|
28
36
|
}
|
|
29
37
|
function currentSelected(s) {
|
|
30
|
-
const displayed = tabIssues(s.issues, s.activeTab);
|
|
38
|
+
const displayed = tabIssues(s.issues, s.activeTab, s.filter);
|
|
31
39
|
const selectedIndex = s.activeTab === "issues" ? s.issuesIndex : s.worktreesIndex;
|
|
32
40
|
return { displayed, selectedIndex };
|
|
33
41
|
}
|
|
@@ -186,7 +194,7 @@ function FooterRow({ phase, overlayKind, latestVersion, listWidth, }) {
|
|
|
186
194
|
// align under the left (list) pane; ticket-specific actions align under
|
|
187
195
|
// the right (detail) pane. Falls back to a single inline row when no
|
|
188
196
|
// width hint is available (e.g. the error path).
|
|
189
|
-
const common = (_jsxs(Text, { children: [_jsx(Text, { bold: true, children: "j/k" }), _jsx(Text, { dimColor: true, children: " nav " }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { bold: true, children: " PgUp/PgDn" }), _jsx(Text, { dimColor: true, children: " scroll " }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { bold: true, children: " r" }), _jsx(Text, { dimColor: true, children: " refresh " }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { bold: true, children: " q" }), _jsx(Text, { dimColor: true, children: " quit" })] }));
|
|
197
|
+
const common = (_jsxs(Text, { children: [_jsx(Text, { bold: true, children: "j/k" }), _jsx(Text, { dimColor: true, children: " nav " }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { bold: true, children: " PgUp/PgDn" }), _jsx(Text, { dimColor: true, children: " scroll " }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { bold: true, children: " r" }), _jsx(Text, { dimColor: true, children: " refresh " }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { bold: true, children: " #" }), _jsx(Text, { dimColor: true, children: " filter " }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { bold: true, children: " q" }), _jsx(Text, { dimColor: true, children: " quit" })] }));
|
|
190
198
|
const ticket = (_jsxs(Text, { children: [_jsx(Text, { bold: true, children: "\u21B5" }), _jsx(Text, { dimColor: true, children: " Switch " }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { bold: true, children: " w" }), _jsx(Text, { dimColor: true, children: " Work " }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { bold: true, children: " o" }), _jsx(Text, { dimColor: true, children: " Open " }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { bold: true, children: " d" }), _jsx(Text, { dimColor: true, children: " Remove" })] }));
|
|
191
199
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: listWidth, children: common }), _jsx(Box, { flexGrow: 1, children: ticket })] }), latestVersion && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "(*)" }), _jsx(Text, { dimColor: true, children: ` new version available — v${latestVersion} · npm i -g mintree` })] }))] }));
|
|
192
200
|
}
|
|
@@ -674,14 +682,15 @@ export default function Dashboard() {
|
|
|
674
682
|
const previousOverlay = prevReady?.overlay ?? null;
|
|
675
683
|
const previousToast = prevReady?.toast ?? null;
|
|
676
684
|
const previousScroll = prevReady?.detailScrollOffset ?? 0;
|
|
677
|
-
const
|
|
678
|
-
const
|
|
685
|
+
const filter = prevReady?.filter ?? "";
|
|
686
|
+
const issuesList = tabIssues(issues, "issues", filter);
|
|
687
|
+
const worktreesList = tabIssues(issues, "worktrees", filter);
|
|
679
688
|
const issuesIndex = Math.min(previousIssuesIndex, Math.max(0, issuesList.length - 1));
|
|
680
689
|
const worktreesIndex = Math.min(previousWorktreesIndex, Math.max(0, worktreesList.length - 1));
|
|
681
690
|
// Preserve scroll only when the active tab's selected issue still
|
|
682
691
|
// resolves to the same row — clamping or list churn means the user
|
|
683
692
|
// is now reading something else.
|
|
684
|
-
const prevDisplayed = prevReady ? tabIssues(prevReady.issues, activeTab) : [];
|
|
693
|
+
const prevDisplayed = prevReady ? tabIssues(prevReady.issues, activeTab, filter) : [];
|
|
685
694
|
const nextDisplayed = activeTab === "issues" ? issuesList : worktreesList;
|
|
686
695
|
const prevSelectedId = prevDisplayed[activeTab === "issues" ? previousIssuesIndex : previousWorktreesIndex]?.issue
|
|
687
696
|
.id ?? null;
|
|
@@ -697,6 +706,7 @@ export default function Dashboard() {
|
|
|
697
706
|
refreshing: false,
|
|
698
707
|
overlay: previousOverlay,
|
|
699
708
|
toast: previousToast,
|
|
709
|
+
filter,
|
|
700
710
|
};
|
|
701
711
|
});
|
|
702
712
|
};
|
|
@@ -818,12 +828,42 @@ export default function Dashboard() {
|
|
|
818
828
|
handleOverlayInput(input, key);
|
|
819
829
|
return;
|
|
820
830
|
}
|
|
831
|
+
// Esc clears an active numeric filter before it falls through to quit —
|
|
832
|
+
// so the user can back out of a search without leaving the dashboard.
|
|
833
|
+
if (key.escape && state.phase === "ready" && state.filter) {
|
|
834
|
+
setState({ ...state, filter: "", issuesIndex: 0, worktreesIndex: 0, detailScrollOffset: 0 });
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
821
837
|
if (input === "q" || key.escape || (input === "c" && key.ctrl)) {
|
|
822
838
|
exit();
|
|
823
839
|
return;
|
|
824
840
|
}
|
|
825
841
|
if (state.phase !== "ready")
|
|
826
842
|
return;
|
|
843
|
+
// Numeric filter: typing a digit narrows the list by ticket number
|
|
844
|
+
// (matched on the digits of the id, so "34" hits both PLA-234 and BE-34).
|
|
845
|
+
// Backspace pops a digit; Esc (handled above) clears it. Reset selection
|
|
846
|
+
// to the first match so the cursor stays on a visible row as it narrows.
|
|
847
|
+
if (/^[0-9]$/.test(input)) {
|
|
848
|
+
setState({
|
|
849
|
+
...state,
|
|
850
|
+
filter: state.filter + input,
|
|
851
|
+
issuesIndex: 0,
|
|
852
|
+
worktreesIndex: 0,
|
|
853
|
+
detailScrollOffset: 0,
|
|
854
|
+
});
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
if ((key.backspace || key.delete) && state.filter) {
|
|
858
|
+
setState({
|
|
859
|
+
...state,
|
|
860
|
+
filter: state.filter.slice(0, -1),
|
|
861
|
+
issuesIndex: 0,
|
|
862
|
+
worktreesIndex: 0,
|
|
863
|
+
detailScrollOffset: 0,
|
|
864
|
+
});
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
827
867
|
if (key.leftArrow || key.rightArrow) {
|
|
828
868
|
// Two tabs only — either arrow toggles. Per-tab indices are
|
|
829
869
|
// preserved, so the user returns to the row they left.
|
|
@@ -1172,7 +1212,7 @@ export default function Dashboard() {
|
|
|
1172
1212
|
if (state.phase === "error") {
|
|
1173
1213
|
return (_jsxs(Box, { width: columns, height: rows, flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, children: [_jsxs(Text, { color: "red", bold: true, children: ["\u2717 ", state.message] }), state.hint && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "yellow", children: ["\u21B3 ", state.hint] }) })), _jsx(Box, { marginTop: 1, children: _jsx(FooterRow, { phase: "error" }) })] }));
|
|
1174
1214
|
}
|
|
1175
|
-
const { issues, refreshing, overlay, toast, activeTab } = state;
|
|
1215
|
+
const { issues, refreshing, overlay, toast, activeTab, filter } = state;
|
|
1176
1216
|
const { displayed, selectedIndex } = currentSelected(state);
|
|
1177
1217
|
const selected = displayed[selectedIndex] ?? null;
|
|
1178
1218
|
const issuesTabCount = issues.reduce((n, d) => (isOrphan(d) ? n : n + 1), 0);
|
|
@@ -1224,7 +1264,9 @@ export default function Dashboard() {
|
|
|
1224
1264
|
: displayed.map((d, index) => ({ kind: "issue", d, index }));
|
|
1225
1265
|
const listView = windowListRows(listRows, selectedIndex, listVisibleRows);
|
|
1226
1266
|
const listContentWidth = Math.max(8, listWidth - 4);
|
|
1227
|
-
return (_jsxs(Box, { flexDirection: "column", width: columns, height: rows, children: [_jsx(Box, { paddingX: 1, paddingTop: 0, flexDirection: "column", children: _jsx(HeaderRow, { repoName: repoName, claudeVersion: claudeVersion, issueCount: issuesTabCount, worktreeCount: worktreesTabCount, activeTab: activeTab, updateAvailable: latestVersion !== null }) }), overlay ? (_jsx(Box, { flexGrow: 1, flexDirection: "column", borderStyle: "round", borderColor: overlay.kind === "remove" ? "yellow" : "cyan", children: overlay.kind === "create" ? (_jsx(CreateOverlayView, { overlay: overlay, onDescChange: onOverlayDescChange, onPromptChange: onOverlayPromptChange })) : (_jsx(RemoveOverlayView, { overlay: overlay })) })) : (_jsxs(Box, { flexGrow: 1, flexDirection: "row", children: [_jsx(Box, { width: listWidth, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: displayed.length === 0 ? (_jsx(Text, { dimColor: true, children:
|
|
1228
|
-
?
|
|
1229
|
-
:
|
|
1267
|
+
return (_jsxs(Box, { flexDirection: "column", width: columns, height: rows, children: [_jsx(Box, { paddingX: 1, paddingTop: 0, flexDirection: "column", children: _jsx(HeaderRow, { repoName: repoName, claudeVersion: claudeVersion, issueCount: issuesTabCount, worktreeCount: worktreesTabCount, activeTab: activeTab, updateAvailable: latestVersion !== null }) }), overlay ? (_jsx(Box, { flexGrow: 1, flexDirection: "column", borderStyle: "round", borderColor: overlay.kind === "remove" ? "yellow" : "cyan", children: overlay.kind === "create" ? (_jsx(CreateOverlayView, { overlay: overlay, onDescChange: onOverlayDescChange, onPromptChange: onOverlayPromptChange })) : (_jsx(RemoveOverlayView, { overlay: overlay })) })) : (_jsxs(Box, { flexGrow: 1, flexDirection: "row", children: [_jsx(Box, { width: listWidth, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: displayed.length === 0 ? (_jsx(Text, { dimColor: true, children: filter
|
|
1268
|
+
? `No tickets match #${filter} — Esc to clear the filter.`
|
|
1269
|
+
: activeTab === "issues"
|
|
1270
|
+
? "No open issues assigned to you in this repo."
|
|
1271
|
+
: "No orphaned worktrees — anything in `.mintree/worktrees/` matches an open issue." })) : (_jsxs(_Fragment, { children: [listView.sticky.map((row, i) => (_jsx(ListRowView, { row: row, selectedIndex: selectedIndex, identifierWidth: identifierWidth, width: listContentWidth }, `sticky-${i}`))), listView.issuesAbove > 0 && (_jsxs(Text, { dimColor: true, children: ["\u2191 ", listView.issuesAbove, " more above"] })), listView.body.map((row, i) => (_jsx(ListRowView, { row: row, selectedIndex: selectedIndex, identifierWidth: identifierWidth, width: listContentWidth }, `body-${i}`))), listView.issuesBelow > 0 && (_jsxs(Text, { dimColor: true, children: ["\u2193 ", listView.issuesBelow, " more below"] }))] })) }), _jsx(Box, { width: detailWidth, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: _jsx(DetailPane, { d: selected, contentWidth: detailWidth - 4, contentHeight: detailContentHeight, scrollOffset: state.detailScrollOffset }) })] })), _jsxs(Box, { paddingX: 1, flexDirection: "column", children: [filter && (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", bold: true, children: `⌕ #${filter}` }), _jsx(Text, { dimColor: true, children: ` · ${displayed.length} match${displayed.length === 1 ? "" : "es"} · Esc clear` })] })), toast && (_jsx(Box, { children: _jsxs(Text, { color: toast.kind === "success" ? "green" : toast.kind === "error" ? "red" : "cyan", children: [toast.kind === "success" ? "✓ " : toast.kind === "error" ? "✗ " : "· ", toast.text] }) })), refreshing && (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { dimColor: true, children: " refreshing" })] })), _jsx(FooterRow, { phase: "ready", overlayKind: overlay?.kind, latestVersion: latestVersion, listWidth: listWidth })] })] }));
|
|
1230
1272
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const description = "Update mintree to the latest version (npm i -g mintree)";
|
|
3
|
+
export declare const options: z.ZodObject<{
|
|
4
|
+
force: z.ZodDefault<z.ZodBoolean>;
|
|
5
|
+
}, z.core.$strip>;
|
|
6
|
+
type Props = {
|
|
7
|
+
options: z.infer<typeof options>;
|
|
8
|
+
};
|
|
9
|
+
export default function Update({ options: opts }: Props): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
import Spinner from "ink-spinner";
|
|
5
|
+
import { option } from "pastel";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { createRequire } from "module";
|
|
8
|
+
import { getLatestVersion, isNewerVersion } from "../lib/version.js";
|
|
9
|
+
import { installLatest, PACKAGE_NAME } from "../lib/update.js";
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const { version: currentVersion } = require("../../package.json");
|
|
12
|
+
export const description = "Update mintree to the latest version (npm i -g mintree)";
|
|
13
|
+
export const options = z.object({
|
|
14
|
+
force: z
|
|
15
|
+
.boolean()
|
|
16
|
+
.default(false)
|
|
17
|
+
.describe(option({
|
|
18
|
+
description: "Reinstall even when you're already on the latest version",
|
|
19
|
+
alias: "f",
|
|
20
|
+
})),
|
|
21
|
+
});
|
|
22
|
+
export default function Update({ options: opts }) {
|
|
23
|
+
const [phase, setPhase] = useState({ kind: "checking" });
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
let cancelled = false;
|
|
26
|
+
(async () => {
|
|
27
|
+
const latest = await getLatestVersion(PACKAGE_NAME);
|
|
28
|
+
if (cancelled)
|
|
29
|
+
return;
|
|
30
|
+
// Skip the reinstall only when we're provably current and the user
|
|
31
|
+
// didn't force it. A null probe (offline/private registry) falls
|
|
32
|
+
// through to the install so `mt update` still does something useful.
|
|
33
|
+
if (!opts.force && latest && !isNewerVersion(currentVersion, latest)) {
|
|
34
|
+
setPhase({ kind: "uptodate", latest });
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
setPhase({ kind: "installing", latest });
|
|
38
|
+
const result = await installLatest();
|
|
39
|
+
if (cancelled)
|
|
40
|
+
return;
|
|
41
|
+
setPhase({ kind: "done", result, latest });
|
|
42
|
+
})();
|
|
43
|
+
return () => {
|
|
44
|
+
cancelled = true;
|
|
45
|
+
};
|
|
46
|
+
}, [opts.force]);
|
|
47
|
+
if (phase.kind === "checking") {
|
|
48
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { children: [" Checking for updates... (current v", currentVersion, ")"] })] }));
|
|
49
|
+
}
|
|
50
|
+
if (phase.kind === "uptodate") {
|
|
51
|
+
return (_jsxs(Box, { flexDirection: "column", paddingY: 0, children: [_jsxs(Text, { color: "green", children: ["\u2713 mintree is already up to date (v", phase.latest, ")."] }), _jsx(Text, { dimColor: true, children: "Run with --force to reinstall anyway." })] }));
|
|
52
|
+
}
|
|
53
|
+
if (phase.kind === "installing") {
|
|
54
|
+
const target = phase.latest ? `v${phase.latest}` : "latest";
|
|
55
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { children: [" ", "Updating mintree from v", currentVersion, " to ", target, "..."] })] }));
|
|
56
|
+
}
|
|
57
|
+
// done
|
|
58
|
+
const { result, latest } = phase;
|
|
59
|
+
if (result.ok) {
|
|
60
|
+
const target = latest ? `v${latest}` : "the latest version";
|
|
61
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "green", children: ["\u2713 mintree updated to ", target, "."] }), _jsx(Text, { dimColor: true, children: "Open a new shell (or re-run your command) to use it." })] }));
|
|
62
|
+
}
|
|
63
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "red", children: "\u2717 Update failed." }), _jsx(Text, { children: result.message }), result.hint ? _jsx(Text, { dimColor: true, children: result.hint }) : null] }));
|
|
64
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const PACKAGE_NAME = "mintree";
|
|
2
|
+
export type UpdateResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
output: string;
|
|
5
|
+
} | {
|
|
6
|
+
ok: false;
|
|
7
|
+
message: string;
|
|
8
|
+
hint?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Reinstalls `mintree@latest` globally via npm. Returns a discriminated result
|
|
12
|
+
* so the command can render a precise message instead of dumping a raw stack.
|
|
13
|
+
* The common failure — EACCES on a root-owned global prefix — gets a targeted
|
|
14
|
+
* hint pointing at the usual fixes.
|
|
15
|
+
*/
|
|
16
|
+
export declare function installLatest(): Promise<UpdateResult>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Self-update: reinstall the globally-installed mintree from npm. The CLI is
|
|
2
|
+
// distributed via `npm i -g mintree`, so updating is just re-running that
|
|
3
|
+
// install for the `@latest` tag. We shell out to `npm` rather than reuse the
|
|
4
|
+
// registry probe in version.ts because npm owns the global prefix, perms, and
|
|
5
|
+
// bin-linking we can't replicate reliably here.
|
|
6
|
+
import { exec } from "child_process";
|
|
7
|
+
import { promisify } from "util";
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
// npm global installs can be slow on a cold cache; give them room before we
|
|
10
|
+
// give up. 2 minutes mirrors what a fresh `npm i -g` typically needs.
|
|
11
|
+
const INSTALL_TIMEOUT_MS = 120_000;
|
|
12
|
+
export const PACKAGE_NAME = "mintree";
|
|
13
|
+
/**
|
|
14
|
+
* Reinstalls `mintree@latest` globally via npm. Returns a discriminated result
|
|
15
|
+
* so the command can render a precise message instead of dumping a raw stack.
|
|
16
|
+
* The common failure — EACCES on a root-owned global prefix — gets a targeted
|
|
17
|
+
* hint pointing at the usual fixes.
|
|
18
|
+
*/
|
|
19
|
+
export async function installLatest() {
|
|
20
|
+
try {
|
|
21
|
+
const { stdout, stderr } = await execAsync(`npm install -g ${PACKAGE_NAME}@latest`, {
|
|
22
|
+
timeout: INSTALL_TIMEOUT_MS,
|
|
23
|
+
});
|
|
24
|
+
return { ok: true, output: (stdout || stderr || "").trim() };
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
28
|
+
return { ok: false, message, hint: hintForError(message) };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function hintForError(message) {
|
|
32
|
+
const m = message.toLowerCase();
|
|
33
|
+
if (m.includes("eacces") || m.includes("permission denied")) {
|
|
34
|
+
return "npm couldn't write to its global prefix. Either fix the prefix ownership (npm docs: 'resolving EACCES permissions errors') or re-run with sudo.";
|
|
35
|
+
}
|
|
36
|
+
if (m.includes("command not found") || m.includes("not recognized")) {
|
|
37
|
+
return "npm wasn't found on your PATH. Install Node.js (which bundles npm) and try again.";
|
|
38
|
+
}
|
|
39
|
+
if (m.includes("etimedout") || m.includes("network") || m.includes("enotfound")) {
|
|
40
|
+
return "Looks like a network problem reaching the npm registry. Check your connection and retry.";
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
package/package.json
CHANGED