@rmdes/indiekit-endpoint-github 1.2.7 → 1.2.8

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/index.js CHANGED
@@ -12,6 +12,8 @@ import { dashboardController } from "./lib/controllers/dashboard.js";
12
12
  import { featuredController } from "./lib/controllers/featured.js";
13
13
  import { starsController } from "./lib/controllers/stars.js";
14
14
  import { starredController } from "./lib/controllers/starred.js";
15
+ import { reposController } from "./lib/controllers/repos.js";
16
+ import { GITHUB_BLOCKS } from "./lib/blocks.js";
15
17
 
16
18
  // Module-level routers (matching Indiekit's endpoint pattern)
17
19
  const protectedRouter = express.Router();
@@ -66,6 +68,10 @@ export default class GitHubEndpoint {
66
68
  };
67
69
  }
68
70
 
71
+ get blocks() {
72
+ return GITHUB_BLOCKS;
73
+ }
74
+
69
75
  /**
70
76
  * Protected routes (require authentication)
71
77
  * HTML pages for admin dashboard
@@ -99,6 +105,7 @@ export default class GitHubEndpoint {
99
105
  publicRouter.get("/api/changelog", changelogController.api);
100
106
  publicRouter.get("/api/starred/all", starredController.all);
101
107
  publicRouter.get("/api/starred/recent", starredController.recent);
108
+ publicRouter.get("/api/repos", reposController.api);
102
109
 
103
110
  return publicRouter;
104
111
  }
package/lib/blocks.js ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * GitHub v2 block declaration (Phase 7c — plugin block ownership).
3
+ *
4
+ * The `github-repos` sidebar widget was a site-config BUILTIN_BLOCKS seed
5
+ * (requiresPlugin null). Declaring it here makes site-config's scanPlugins stamp
6
+ * `sourcePlugin` → `requiresPlugin` ("GitHub activity endpoint"), so the block is
7
+ * properly plugin-gated (theme ENDPOINT_SLUGS maps it to the `github` loadout
8
+ * slug). scanPlugins precedence is `built-in < plugin blocks`, so this entry
9
+ * OVERWRITES the builtin seed where the plugin is loaded; the seed itself is
10
+ * removed from site-config in Phase 7d alongside the legacy-map bridge.
11
+ *
12
+ * `source:"api"` is honest: the widget fetches the plugin's JSON API LIVE
13
+ * client-side (commits/featured/contributions/repos) — no rebuild needed for
14
+ * fresh data. As of 7c the Repos tab also routes through this plugin
15
+ * (/api/repos) instead of hitting api.github.com directly from the browser, so
16
+ * the plugin is the single source of truth. Bespoke template: the theme owns
17
+ * `components/widgets/github-repos.njk` + `js/widgets/github-repos.js`.
18
+ *
19
+ * @module lib/blocks
20
+ */
21
+
22
+ /** @type {Array<object>} */
23
+ export const GITHUB_BLOCKS = [
24
+ {
25
+ id: "github-repos",
26
+ version: 1,
27
+ label: "GitHub Projects",
28
+ description: "GitHub repositories and activity",
29
+ icon: "github",
30
+ category: "social",
31
+ placement: { regions: ["sidebar"], surfaces: ["homepage"] },
32
+ multiple: false,
33
+ data: { source: "api" },
34
+ schema: { type: "object", additionalProperties: false, properties: {} },
35
+ },
36
+ ];
@@ -0,0 +1,57 @@
1
+ import { GitHubClient } from "../github-client.js";
2
+
3
+ /**
4
+ * Public JSON API for the user's own repositories (Phase 7c).
5
+ *
6
+ * The sidebar widget's "Repos" tab previously fetched api.github.com DIRECTLY
7
+ * from the browser (sort=updated, type=owner) and filtered fork/private
8
+ * client-side — bypassing this plugin, so the GitHub token never protected the
9
+ * call (rate limits, no auth). This endpoint moves that fetch server-side: the
10
+ * plugin (GitHubClient + token + 15-min cache) is now the single source of truth.
11
+ *
12
+ * Returns a SLIM shape using the RAW GitHub field names the widget template
13
+ * already reads (html_url/name/description/language/stargazers_count/updated_at),
14
+ * so github-repos.njk needs no change — only the widget's fetch URL moves to
15
+ * /api/repos.
16
+ * @type {import("express").RequestHandler}
17
+ */
18
+ export const reposController = {
19
+ async api(request, response, next) {
20
+ try {
21
+ const { username, token, cacheTtl, limits } =
22
+ request.app.locals.application.githubConfig;
23
+
24
+ if (!username) {
25
+ return response.status(400).json({ error: "No username configured" });
26
+ }
27
+
28
+ const client = new GitHubClient({ token, cacheTtl });
29
+
30
+ let raw = [];
31
+ try {
32
+ // Fetch extra (×2) so the fork/private filter still yields enough.
33
+ raw = await client.getUserRepos(username, (limits.repos || 10) * 2, "updated");
34
+ } catch (apiError) {
35
+ return response
36
+ .status(apiError.status || 500)
37
+ .json({ error: apiError.message });
38
+ }
39
+
40
+ const repos = (Array.isArray(raw) ? raw : [])
41
+ .filter((r) => r && !r.fork && !r.private)
42
+ .slice(0, limits.repos || 10)
43
+ .map((r) => ({
44
+ name: r.name,
45
+ html_url: r.html_url,
46
+ description: r.description,
47
+ language: r.language,
48
+ stargazers_count: r.stargazers_count,
49
+ updated_at: r.updated_at,
50
+ }));
51
+
52
+ response.json({ repos });
53
+ } catch (error) {
54
+ next(error);
55
+ }
56
+ },
57
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-github",
3
- "version": "1.2.7",
3
+ "version": "1.2.8",
4
4
  "description": "GitHub activity endpoint for Indiekit. Display commits, stars, contributions, and featured repositories.",
5
5
  "keywords": [
6
6
  "indiekit",