Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 9 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,32 @@

---

## 2026.01.23
## 1/23/26

### Payments
Introduced a redesigned payments onboarding flow
![Payments Onboarding]()

## 2026.01.21
## 1/21/26

### Payments
- Payments page updated with new UI changes
![Create Product]()
- Added a new Payments Settings page with an option to temporarily disable all payments
![Payments Setting]()
- Subscription renewal emails are now sent automatically to users
- Past payment invoices are now visible on the Account Settings page
![Past Payments Invoices]()

### Documentation
- Updated JWT documentation to include `isRestricted` and `restrictedReason`

## 2026.01.19
## 1/19/26
- Updated package dependencies to their newest versions.

## 2025.12.19
## 12/19/25
- Introduces new changelog and deprecates all older changelogs.
- Moved away from semantic versioning in favor of CalVer.
- Date versioning for public view.

---

Expand Down
28 changes: 24 additions & 4 deletions apps/backend/src/app/api/latest/internal/changelog/route.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { createSmartRouteHandler } from "@/route-handlers/smart-route-handler";
import { yupArray, yupBoolean, yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env";
import { getEnvVariable, getNodeEnvironment } from "@stackframe/stack-shared/dist/utils/env";
import * as fs from "fs/promises";
import * as path from "path";

const REVALIDATE_SECONDS = 60 * 60;

Expand Down Expand Up @@ -72,10 +74,9 @@ function parseRootChangelog(markdown: string): ChangelogEntry[] {

const heading = versionMatch[1].trim();
const { version, releasedAt, isUnreleased } = parseVersionHeading(heading);
const isSemver = /^\d+\.\d+\.\d+$/.test(version);
const isCalVer = /^\d{4}\.\d{2}\.\d{2}$/.test(version);
const isUsDate = /^\d{1,2}\/\d{1,2}\/\d{2}$/.test(version); // US date format: M/D/YY

if (!isUnreleased && !isSemver && !isCalVer) {
if (!isUnreleased && !isUsDate) {
continue;
}

Expand Down Expand Up @@ -138,6 +139,25 @@ export const GET = createSmartRouteHandler({
}).defined(),
}),
handler: async () => {
const isDevelopment = getNodeEnvironment() === "development";

// In development mode, read from local CHANGELOG.md file
if (isDevelopment) {
const changelogPath = path.resolve(process.cwd(), "../../CHANGELOG.md");
const fileExists = await fs.access(changelogPath).then(() => true, () => false);

if (fileExists) {
const content = await fs.readFile(changelogPath, "utf-8");
const entries = parseRootChangelog(content).slice(0, 8);

return {
statusCode: 200,
bodyType: "json",
body: { entries },
} as const;
}
}

const changelogUrl = getEnvVariable("STACK_CHANGELOG_URL", "");

if (!changelogUrl) {
Expand Down
6 changes: 6 additions & 0 deletions apps/dashboard/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ const nextConfig = {
port: '',
pathname: '/**',
},
{
protocol: 'https',
hostname: 'raw.githubusercontent.com',
port: '',
pathname: '/**',
},
],
},

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 13 additions & 10 deletions apps/dashboard/src/components/stack-companion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,22 @@ import { FeatureRequestBoard } from './stack-companion/feature-request-board';
import { UnifiedDocsWidget } from './stack-companion/unified-docs-widget';

/**
* Compare two CalVer versions in YYYY.MM.DD format
* Compare two US date versions in M/D/YY format
* Returns true if version1 is newer than version2
*/
function isNewerCalVer(version1: string, version2: string): boolean {
const parseCalVer = (version: string): Date | null => {
const match = version.match(/^(\d{4})\.(\d{2})\.(\d{2})$/);
function isNewerVersion(version1: string, version2: string): boolean {
const parseUsDate = (version: string): Date | null => {
const match = version.match(/^(\d{1,2})\/(\d{1,2})\/(\d{2})$/);
if (!match) return null;
const [, year, month, day] = match;
return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
const [, month, day, year] = match;
const twoDigitYear = parseInt(year);
// Sliding window: 70-99 → 1970-1999, 00-69 → 2000-2069
const fullYear = twoDigitYear >= 70 ? 1900 + twoDigitYear : 2000 + twoDigitYear;
return new Date(fullYear, parseInt(month) - 1, parseInt(day));
};

const date1 = parseCalVer(version1);
const date2 = parseCalVer(version2);
const date1 = parseUsDate(version1);
const date2 = parseUsDate(version2);

if (!date1 || !date2) {
// Fallback to string comparison if parsing fails
Expand Down Expand Up @@ -193,7 +196,7 @@ export function StackCompanion({ className }: { className?: string }) {
} else {
const hasNewer = entries.some((entry: ChangelogEntry) => {
if (entry.isUnreleased) return false;
return isNewerCalVer(entry.version, lastSeen);
return isNewerVersion(entry.version, lastSeen);
});
setHasNewVersions(hasNewer);
}
Expand Down Expand Up @@ -231,7 +234,7 @@ export function StackCompanion({ className }: { className?: string }) {
} else {
const hasNewer = changelogData.some((entry: ChangelogEntry) => {
if (entry.isUnreleased) return false;
return isNewerCalVer(entry.version, lastSeen);
return isNewerVersion(entry.version, lastSeen);
});
setHasNewVersions(hasNewer);
}
Expand Down
Loading