Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fef8e90
feat(core): migrate common/ render, ops, http, calculateRank to TypeS…
marcalexiei Jul 3, 2026
748d8c5
refactor(frontend): replace eslint react plugins with `@eslint-react/…
marcalexiei Jul 3, 2026
ed9d284
feat(frontend): build card URLs with a typed per-card builder (#328)
marcalexiei Jul 5, 2026
5da671b
ci: install only `chromium` for Playwright e2e (#329)
marcalexiei Jul 5, 2026
a2bb443
several dependency upgrades (#334)
martin-mfg Jul 6, 2026
536199b
several dependency upgrades again (#336)
martin-mfg Jul 6, 2026
6f1ad4b
Update languages JSON (#321)
github-actions[bot] Jul 6, 2026
807aa45
ci(deps): Bump actions/checkout from 6.0.3 to 7.0.0 (#299)
dependabot[bot] Jul 6, 2026
4043488
adjust repeat-recent (#341)
martin-mfg Jul 7, 2026
1d9db07
fix color of theme switcher (#343)
martin-mfg Jul 10, 2026
7c7fca4
Fix link to calculateRank source file in docs (#351)
shaurya-sharma-dev Jul 11, 2026
b0100ca
fix(core): validate username / gist id against the safe-character pat…
marcalexiei Jul 12, 2026
b81abe0
refactor(frontend): consolidate Home card options into a single state…
marcalexiei Jul 12, 2026
4ccec46
fix warnings (#342)
martin-mfg Jul 12, 2026
3258ad0
longer prerender caching (#360)
martin-mfg Jul 13, 2026
2007f51
small test improvements (#386)
martin-mfg Jul 22, 2026
63ca91e
feat(core): start fetchers TypeScript migration (retryer generic + gi…
marcalexiei Jul 23, 2026
2b908ad
fix: update Vercel deploy link to point to the backend directory (#382)
shuaixr Jul 23, 2026
1d7ea96
switch from stargazers.totalCount to stargazerCount (#391)
martin-mfg Jul 23, 2026
8f7cc58
longer prerender caching 2 (#393)
martin-mfg Jul 23, 2026
3bda3ab
bump core version to 2.1.4 (#399)
martin-mfg Jul 24, 2026
90508f0
upgrade several dependencies (#400)
martin-mfg Jul 25, 2026
9201b65
ci(deps): Bump actions/checkout from 7.0.0 to 7.0.1 (#395)
dependabot[bot] Jul 25, 2026
2992f63
ci(deps): Bump fjogeleit/http-request-action from 2.0.0 to 2.0.1 (#362)
dependabot[bot] Jul 25, 2026
1c6b4c3
fix dropdown bugs and gradient bug (#402)
martin-mfg Jul 26, 2026
caccc4d
bump core version to 2.1.5 (#407)
martin-mfg Jul 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install Dependencies
uses: ./.github/actions/install-dependencies
Expand Down Expand Up @@ -59,16 +59,18 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install Dependencies
uses: ./.github/actions/install-dependencies

- name: Build packages
run: pnpm run build:packages

# Only install chromium since it is the only browser enabled in
# apps/frontend/playwright.config.ts. Update this if more projects are added.
- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
run: pnpm exec playwright install --with-deps chromium

- name: Run Playwright tests
run: pnpm --filter ./apps/frontend/ run test:e2e
Expand All @@ -85,7 +87,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install Dependencies
uses: ./.github/actions/install-dependencies
Expand All @@ -106,7 +108,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install Dependencies
uses: ./.github/actions/install-dependencies
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/generate-theme-doc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install Dependencies
uses: ./.github/actions/install-dependencies
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install Dependencies
uses: ./.github/actions/install-dependencies
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/repeat-recent-requests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ on:
# │ │ │ │ │
# │ │ │ │ │
# * * * * *
- cron: "45 * * * *"
- cron: "15,45 * * * *"
workflow_dispatch:

permissions: {}

jobs:
triggerRepeatRecent:
if: |
Expand All @@ -24,7 +26,7 @@ jobs:
steps:
- name: Make Request
id: myRequest
uses: fjogeleit/http-request-action@551353b829c3646756b2ec2b3694f819d7957495 # v2
uses: fjogeleit/http-request-action@f98bb26551cd1af7d3eb2674578a80754ece88db # v2
with:
url: "https://github-stats-extended.vercel.app/api/repeat-recent"
method: "POST"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/update-langs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install Dependencies
uses: ./.github/actions/install-dependencies
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/update-tags.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: haya14busa/action-update-semver@7d2c558640ea49e798d46539536190aff8c18715 # v1.5.1
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": {
"editor.tabSize": 2,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"cSpell.words": ["Wakatime"]
"cSpell.words": ["retryer", "Wakatime"]
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"expiration": 39600,
"expiration": 255600,
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
"passQuery": true
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"expiration": 39600,
"expiration": 255600,
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
"passQuery": true
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"expiration": 39600,
"expiration": 255600,
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
"passQuery": true
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"expiration": 39600,
"expiration": 255600,
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
"passQuery": true
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"expiration": 39600,
"expiration": 255600,
"bypassToken": "r3fr3shT0k3n-r3fr3shT0k3n-r3fr3shT0k3n",
"passQuery": true
}
4 changes: 2 additions & 2 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
"vitest": "catalog:default"
},
"dependencies": {
"axios": "catalog:default",
"@stats-organization/github-readme-stats-core": "workspace:^",
"pg": "^8.21.0"
"axios": "catalog:default",
"pg": "^8.22.0"
}
}
2 changes: 1 addition & 1 deletion apps/backend/src/repeatRequests.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,6 @@ export async function repeatRecentRequests() {
if (urls.length === 0) {
console.log("No recent requests found.");
} else {
await makeRequests(urls, 5);
await makeRequests(urls, 3);
}
}
6 changes: 2 additions & 4 deletions apps/backend/tests/e2e/e2e.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const STATS_MOCK_RESPONSE = {
followers: { totalCount: 0 },
repositories: {
totalCount: 1,
nodes: [{ name: REPO, stargazers: { totalCount: 1 } }],
nodes: [{ name: REPO, stargazerCount: 1 }],
pageInfo: {
hasNextPage: false,
endCursor: "cursor",
Expand Down Expand Up @@ -110,9 +110,7 @@ const REPO_MOCK_RESPONSE = {
isPrivate: false,
isArchived: false,
isTemplate: false,
stargazers: {
totalCount: 1,
},
stargazerCount: 1,
description: "Simple cra test repo.",
primaryLanguage: {
color: "#f1e05a",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ exports[`Test /api contract > should match the public many-params response snaps

</g>
</svg>",
"graphqlRequest": "{"query":"\\n query userInfo($login: String!, $after: String, $includeMergedPullRequests: Boolean!, $includeDiscussions: Boolean!, $includeDiscussionsAnswers: Boolean!, $startTime: DateTime = null, $ownerAffiliations: [RepositoryAffiliation]) {\\n user(login: $login) {\\n name\\n login\\n commits: contributionsCollection (from: $startTime) {\\n totalCommitContributions,\\n }\\n reviews: contributionsCollection {\\n totalPullRequestReviewContributions\\n }\\n repositoriesContributedTo(first: 1, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {\\n totalCount\\n }\\n pullRequests(first: 1) {\\n totalCount\\n }\\n mergedPullRequests: pullRequests(states: MERGED) @include(if: $includeMergedPullRequests) {\\n totalCount\\n }\\n openIssues: issues(states: OPEN) {\\n totalCount\\n }\\n closedIssues: issues(states: CLOSED) {\\n totalCount\\n }\\n followers {\\n totalCount\\n }\\n repositoryDiscussions @include(if: $includeDiscussions) {\\n totalCount\\n }\\n repositoryDiscussionComments(onlyAnswers: true) @include(if: $includeDiscussionsAnswers) {\\n totalCount\\n }\\n \\n repositories(first: 100, after: $after, ownerAffiliations: $ownerAffiliations, orderBy: {direction: DESC, field: STARGAZERS}) {\\n totalCount\\n nodes {\\n name\\n stargazers {\\n totalCount\\n }\\n }\\n pageInfo {\\n hasNextPage\\n endCursor\\n }\\n }\\n\\n }\\n }\\n","variables":{"login":"anuraghazra","first":100,"after":null,"includeMergedPullRequests":true,"includeDiscussions":true,"includeDiscussionsAnswers":true,"startTime":"2024-01-01T00:00:00Z","ownerAffiliations":["OWNER","COLLABORATOR"]}}",
"graphqlRequest": "{"query":"\\n query userInfo($login: String!, $after: String, $includeMergedPullRequests: Boolean!, $includeDiscussions: Boolean!, $includeDiscussionsAnswers: Boolean!, $startTime: DateTime = null, $ownerAffiliations: [RepositoryAffiliation]) {\\n user(login: $login) {\\n name\\n login\\n commits: contributionsCollection (from: $startTime) {\\n totalCommitContributions,\\n }\\n reviews: contributionsCollection {\\n totalPullRequestReviewContributions\\n }\\n repositoriesContributedTo(first: 1, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {\\n totalCount\\n }\\n pullRequests(first: 1) {\\n totalCount\\n }\\n mergedPullRequests: pullRequests(states: MERGED) @include(if: $includeMergedPullRequests) {\\n totalCount\\n }\\n openIssues: issues(states: OPEN) {\\n totalCount\\n }\\n closedIssues: issues(states: CLOSED) {\\n totalCount\\n }\\n followers {\\n totalCount\\n }\\n repositoryDiscussions @include(if: $includeDiscussions) {\\n totalCount\\n }\\n repositoryDiscussionComments(onlyAnswers: true) @include(if: $includeDiscussionsAnswers) {\\n totalCount\\n }\\n \\n repositories(first: 100, after: $after, ownerAffiliations: $ownerAffiliations, orderBy: {direction: DESC, field: STARGAZERS}) {\\n totalCount\\n nodes {\\n name\\n stargazerCount\\n }\\n pageInfo {\\n hasNextPage\\n endCursor\\n }\\n }\\n\\n }\\n }\\n","variables":{"login":"anuraghazra","first":100,"after":null,"includeMergedPullRequests":true,"includeDiscussions":true,"includeDiscussionsAnswers":true,"startTime":"2024-01-01T00:00:00Z","ownerAffiliations":["OWNER","COLLABORATOR"]}}",
"headers": [
[
"Cache-Control",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ exports[`Test /api/pin contract > should match the public many-params response s

</g>
</svg>",
"graphqlRequest": "{"query":"\\n fragment RepoInfo on Repository {\\n name\\n nameWithOwner\\n isPrivate\\n isArchived\\n isTemplate\\n stargazers {\\n totalCount\\n }\\n description\\n primaryLanguage {\\n color\\n id\\n name\\n }\\n forkCount\\n }\\n query getRepo($login: String!, $repo: String!) {\\n user(login: $login) {\\n repository(name: $repo) {\\n ...RepoInfo\\n }\\n }\\n organization(login: $login) {\\n repository(name: $repo) {\\n ...RepoInfo\\n }\\n }\\n }\\n ","variables":{"login":"anuraghazra","repo":"convoychat"}}",
"graphqlRequest": "{"query":"\\n fragment RepoInfo on Repository {\\n name\\n nameWithOwner\\n isPrivate\\n isArchived\\n isTemplate\\n stargazerCount\\n description\\n primaryLanguage {\\n color\\n id\\n name\\n }\\n forkCount\\n }\\n query getRepo($login: String!, $repo: String!) {\\n user(login: $login) {\\n repository(name: $repo) {\\n ...RepoInfo\\n }\\n }\\n organization(login: $login) {\\n repository(name: $repo) {\\n ...RepoInfo\\n }\\n }\\n }\\n ","variables":{"login":"anuraghazra","repo":"convoychat"}}",
"headers": [
[
"Cache-Control",
Expand Down
10 changes: 4 additions & 6 deletions apps/backend/tests/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ export const data_stats = {
repositories: {
totalCount: 3,
nodes: [
{ name: "repo-keep-1", stargazers: { totalCount: 1500 } },
{ name: "repo-exclude-me", stargazers: { totalCount: 9999 } },
{ name: "repo-keep-2", stargazers: { totalCount: 2600 } },
{ name: "repo-keep-1", stargazerCount: 1500 },
{ name: "repo-exclude-me", stargazerCount: 9999 },
{ name: "repo-keep-2", stargazerCount: 2600 },
],
pageInfo: {
hasNextPage: false,
Expand Down Expand Up @@ -73,9 +73,7 @@ export const data_user = {
username: "anuraghazra",
name: "convoychat",
nameWithOwner: "anuraghazra/convoychat",
stargazers: {
totalCount: 38000,
},
stargazerCount: 38000,
description:
"Help us take over the world with a deeply customizable React, TypeScript and GraphQL chat app that has enough text to wrap across multiple lines in the repository card.",
primaryLanguage: {
Expand Down
61 changes: 61 additions & 0 deletions apps/frontend/e2e/app-trends-auth.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { expect, test } from "@playwright/test";
import type { Page } from "@playwright/test";

/**
* Puts the SPA into an authenticated state without contacting GitHub by
* stubbing the OAuth code exchange and the follow-up user-access lookup:
* `authenticate` returns a userId (which flips `isAuthenticated` to true), and
* `user-access` returns metadata so AppTrends does not immediately log back out.
* @param page - The Playwright page to install the route handlers on.
*/
async function mockAuthEndpoints(page: Page): Promise<void> {
await page.route("**/api/authenticate**", (route) =>
route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ userId: "octocat", needDowngrade: false }),
}),
);
await page.route("**/api/user-access**", (route) =>
route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ token: "test-token", privateAccess: "false" }),
}),
);
}

test.describe("AppTrends auth-driven stage transition", () => {
test("auto-advances from Login to Select a Card once authenticated", async ({
page,
}) => {
await mockAuthEndpoints(page);

// Land on the app as GitHub does after the OAuth redirect (URL carries `code`).
await page.goto("?code=test-oauth-code");

// AppTrends starts unauthenticated on stage 0 ("Login"). When the code
// exchange flips `isAuthenticated` false -> true, the render-phase
// transition must advance the stepper to stage 1 ("Select a Card").
await expect(page.getByRole("heading", { level: 1 })).toContainText(
"Select a Card",
);
});

test("keeps a manually selected earlier step instead of re-forcing it", async ({
page,
}) => {
await mockAuthEndpoints(page);
await page.goto("?code=test-oauth-code");

const heading = page.getByRole("heading", { level: 1 });
await expect(heading).toContainText("Select a Card");

// Going back to "Login" while still authenticated must stick: the transition
// fires only on an auth *change*, so a later render must not snap the user
// forward again. A bug that dropped the previous-value guard would bounce
// the heading straight back to "Select a Card" here.
await page.getByRole("button", { name: "Login" }).click();
await expect(heading).toContainText("Login");
});
});
24 changes: 24 additions & 0 deletions apps/frontend/e2e/stats-rank.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect, test } from "@playwright/test";

test("selecting 'None' progress style hides the rank circle", async ({
page,
}) => {
await page.goto("");

// Stage 0 -> 2: go to customization
await page.getByRole("button", { name: "Modify Parameters" }).click();
await expect(page.getByRole("heading", { level: 1 })).toContainText(
"Modify Card Parameters",
);

const preview = page.locator("#svgWrapper");
const rankCircle = preview.locator('[data-testid="rank-circle"]');

// Default "Rank" progress style renders the rank circle.
await expect(preview).toBeAttached();
await expect(rankCircle).toHaveCount(1);

await page.getByRole("combobox").selectOption({ label: "None" });

await expect(rankCircle).toHaveCount(0);
});
18 changes: 9 additions & 9 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,29 @@
},
"dependencies": {
"@reduxjs/toolkit": "^2.12.0",
"@tailwindcss/vite": "^4.3.0",
"axios": "catalog:default",
"axios-cache-interceptor": "^1",
"daisyui": "^5.5.23",
"@stats-organization/github-readme-stats-backend": "workspace:^",
"@stats-organization/github-readme-stats-core": "workspace:^",
"@tailwindcss/vite": "^4.3.3",
"axios": "catalog:default",
"axios-cache-interceptor": "^1.12.0",
"daisyui": "^5.6.18",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"react-icons": "^5.6.0",
"react-loading-skeleton": "^3.3.1",
"react-icons": "^5.7.0",
"react-loading-skeleton": "^3.5.0",
"react-redux": "^9.3.0",
"react-spinners": "^0.17.0",
"react-toastify": "^11.1.0",
"redux": "^5.0.1",
"save-svg-as-png": "^1.4.17",
"uuid": "^14.0.0"
"uuid": "^14.0.1"
},
"devDependencies": {
"@types/react": "19.2.17",
"@types/react-dom": "19.2.3",
"@vitejs/plugin-react": "6.0.2",
"@vitejs/plugin-react": "6.0.3",
"clsx": "2.1.1",
"tailwindcss": "4.3.0",
"tailwindcss": "4.3.3",
"vite": "catalog:default",
"vitest": "catalog:default"
},
Expand Down
8 changes: 5 additions & 3 deletions apps/frontend/src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { clsx } from "clsx";
import type { CSSProperties, JSX } from "react";

import type { CardUrlBuilder } from "../../models/CardUrl";

import { CardImage } from "./CardImage";
import { LIGHT_CARD_BG } from "./themeBackdrop";

interface CardProps {
title: string;
description: string;
imageSrc: string;
card: CardUrlBuilder;
stage: number;
selected?: boolean;
compact?: boolean;
Expand All @@ -21,7 +23,7 @@ interface CardProps {
export const Card = ({
title,
description,
imageSrc,
card,
stage,
selected = false,
compact = false,
Expand Down Expand Up @@ -68,7 +70,7 @@ export const Card = ({
</h2>
<p className="text-base leading-relaxed mt-2 mb-4">{description}</p>
<CardImage
imageSrc={imageSrc}
card={card}
compact={compact}
className={clsx({ "flex justify-center": fixedSize })}
stage={stage}
Expand Down
Loading
Loading