ripencli 1.2.3 → 1.2.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +71 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -89,7 +89,7 @@ Press `s` to open the settings screen. Settings are persisted at `~/.config/ripe
89
89
  | Grouped scopes | — | List of scopes to group (e.g. `@heroui`, `@radix-ui`) |
90
90
  | SFW Firewall | Off | Prepend `sfw` before every generated command (requires [sfw](https://github.com/SocketDev/sfw-free)) |
91
91
 
92
- When using `ripen -g`, all available package managers (npm, pnpm, yarn) are checked in parallel so you see every global package in one place. Bun is not included in global checking because it doesn't provide a JSON output for its outdated command.
92
+ When using `ripen -g`, all available package managers (npm, pnpm, yarn) (except bun) are checked in parallel so you see every global package in one place. Bun is not included in global checking because it doesn't provide a JSON output for its outdated command.
93
93
 
94
94
  ## License
95
95
 
package/dist/cli.js CHANGED
@@ -106,6 +106,23 @@ function parseBaseVersion(range) {
106
106
  }
107
107
  //#endregion
108
108
  //#region src/registry.ts
109
+ let cachedToken;
110
+ /**
111
+ * Get a GitHub token from the `gh` CLI (`gh auth token`). Unauthenticated
112
+ * requests are limited to 60/hour per IP and are easily exhausted; an
113
+ * authenticated request raises the limit to 5,000/hour. Returns null when
114
+ * `gh` is not installed or the user is not logged in. Cached per process.
115
+ */
116
+ async function githubToken() {
117
+ if (cachedToken !== void 0) return cachedToken;
118
+ try {
119
+ const { stdout, exitCode } = await execa("gh", ["auth", "token"], { reject: false });
120
+ cachedToken = exitCode === 0 && stdout.trim() ? stdout.trim() : null;
121
+ } catch {
122
+ cachedToken = null;
123
+ }
124
+ return cachedToken;
125
+ }
109
126
  async function fetchVersions(packageName) {
110
127
  try {
111
128
  const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`);
@@ -135,11 +152,17 @@ async function fetchVersions(packageName) {
135
152
  async function fetchChangelog(packageName, fromVersion, toVersion) {
136
153
  try {
137
154
  const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`);
138
- if (!res.ok) return [];
155
+ if (!res.ok) return { entries: [] };
139
156
  const repo = extractGitHubRepo(await res.json());
140
- if (!repo) return [];
141
- const ghRes = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=30`, { headers: { Accept: "application/vnd.github+json" } });
142
- if (!ghRes.ok) return [];
157
+ if (!repo) return { entries: [] };
158
+ const token = await githubToken();
159
+ const headers = { Accept: "application/vnd.github+json" };
160
+ if (token) headers.Authorization = `Bearer ${token}`;
161
+ const ghRes = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=30`, { headers });
162
+ if (!ghRes.ok) return {
163
+ entries: [],
164
+ rateLimited: (ghRes.status === 403 || ghRes.status === 429) && !token
165
+ };
143
166
  const releases = await ghRes.json();
144
167
  const toMajor = parseVersion(toVersion)[0];
145
168
  const filtered = releases.filter((r) => {
@@ -153,15 +176,15 @@ async function fetchChangelog(packageName, fromVersion, toVersion) {
153
176
  }));
154
177
  if (filtered.length === 0 && releases.length > 0) {
155
178
  const latest = releases[0];
156
- return [{
179
+ return { entries: [{
157
180
  version: latest.tag_name,
158
181
  body: latest.body?.trim() ?? "No release notes.",
159
182
  url: latest.html_url
160
- }];
183
+ }] };
161
184
  }
162
- return filtered.sort((a, b) => compareVersions(a.version, b.version));
185
+ return { entries: filtered.sort((a, b) => compareVersions(a.version, b.version)) };
163
186
  } catch {
164
- return [];
187
+ return { entries: [] };
165
188
  }
166
189
  }
167
190
  async function fetchLatestVersion(packageName) {
@@ -1693,6 +1716,7 @@ function MarkdownLine({ line, baseColor = "white", repoUrl }) {
1693
1716
  //#region src/ui/ChangelogPanel.tsx
1694
1717
  function ChangelogPanel({ pkg, onClose }) {
1695
1718
  const [entries, setEntries] = useState([]);
1719
+ const [rateLimited, setRateLimited] = useState(false);
1696
1720
  const [repoUrl, setRepoUrl] = useState("");
1697
1721
  const [loading, setLoading] = useState(true);
1698
1722
  const [opened, setOpened] = useState(false);
@@ -1701,9 +1725,10 @@ function ChangelogPanel({ pkg, onClose }) {
1701
1725
  const { columns, rows } = useWindowSize();
1702
1726
  const isUpToDate = pkg.current === (pkg.targetVersion ?? pkg.latest);
1703
1727
  useEffect(() => {
1704
- Promise.all([fetchChangelog(pkg.name, isUpToDate ? "" : pkg.current, pkg.targetVersion ?? pkg.latest), fetchRepoUrl(pkg.name)]).then(([e, repo]) => {
1705
- setEntries(e);
1706
- setActiveEntry(isUpToDate ? Math.max(0, e.length - 1) : 0);
1728
+ Promise.all([fetchChangelog(pkg.name, isUpToDate ? "" : pkg.current, pkg.targetVersion ?? pkg.latest), fetchRepoUrl(pkg.name)]).then(([result, repo]) => {
1729
+ setEntries(result.entries);
1730
+ setRateLimited(result.rateLimited ?? false);
1731
+ setActiveEntry(isUpToDate ? Math.max(0, result.entries.length - 1) : 0);
1707
1732
  setRepoUrl(repo);
1708
1733
  setLoading(false);
1709
1734
  });
@@ -1828,6 +1853,41 @@ function ChangelogPanel({ pkg, onClose }) {
1828
1853
  loading ? /* @__PURE__ */ jsx(Text, {
1829
1854
  color: "gray",
1830
1855
  children: " fetching release notes…"
1856
+ }) : rateLimited ? /* @__PURE__ */ jsxs(Box, {
1857
+ flexDirection: "column",
1858
+ children: [
1859
+ /* @__PURE__ */ jsx(Text, {
1860
+ color: "yellow",
1861
+ children: " GitHub rate limit reached (60 requests/hour for unauthenticated use)."
1862
+ }),
1863
+ /* @__PURE__ */ jsx(Text, {
1864
+ color: "gray",
1865
+ children: " Install the GitHub CLI and log in to raise the limit to 5,000/hour:"
1866
+ }),
1867
+ /* @__PURE__ */ jsxs(Text, {
1868
+ color: "gray",
1869
+ children: [
1870
+ " ",
1871
+ /* @__PURE__ */ jsx(Text, {
1872
+ color: "white",
1873
+ children: "gh auth login"
1874
+ }),
1875
+ " — https://cli.github.com"
1876
+ ]
1877
+ }),
1878
+ releasesPageUrl && /* @__PURE__ */ jsxs(Text, {
1879
+ color: "gray",
1880
+ children: [
1881
+ " ",
1882
+ "Or press ",
1883
+ /* @__PURE__ */ jsx(Text, {
1884
+ color: "white",
1885
+ children: "r"
1886
+ }),
1887
+ " to open the releases page in browser."
1888
+ ]
1889
+ })
1890
+ ]
1831
1891
  }) : entries.length === 0 ? /* @__PURE__ */ jsxs(Box, {
1832
1892
  flexDirection: "column",
1833
1893
  children: [/* @__PURE__ */ jsx(Text, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ripencli",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "Interactive dependency updater for npm, pnpm, yarn, and bun",
5
5
  "license": "MIT",
6
6
  "author": {