mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
c7d71ea57c
* feat(whats-new): remote release notes with dev workspace mode Add WHATS_NEW.md, CI whats-new.md asset upload, and client fetch/cache with embedded fallbacks. Dev and -dev builds read the full file from the repo for debugging; RC/stable download the release asset on first use. * fix(whats-new): render ## headings and add changelog tab Parse h2 sections in release-notes markdown; load changelog alongside highlights and let users switch views on the What's New page. * fix(whats-new): prefetch on startup and fix CI typecheck prebuild Prefetch whats-new asset when the shell loads on RC/stable builds. Run prebuild:release-notes before tsc and coverage jobs so the gitignored generated bundle exists in CI. * docs: CHANGELOG and credits for What's New remote notes (PR #1058) * fix(whats-new): always slice embedded release notes to current line Drop full CHANGELOG embed for -dev bundles; tauri:dev still reads live markdown from the repo. Ignore all of src/generated/ in git. * fix(whats-new): fetch release asset via Rust to bypass CORS Route whats-new.md download through fetch_url_bytes; rename the technical tab label; add fetch unit tests (PR #1058 review).
124 lines
4.0 KiB
TypeScript
124 lines
4.0 KiB
TypeScript
import { isWorkspaceReleaseNotesMode } from './releaseNotesChannel';
|
|
import { readReleaseNotesCache, writeReleaseNotesCache } from './releaseNotesCache';
|
|
import { fetchWhatsNewAsset } from './releaseNotesFetch';
|
|
import { findReleaseNotesEntry, type ReleaseNotesEntry } from './releaseNotesMatch';
|
|
|
|
export type ReleaseNotesSource =
|
|
| 'workspace'
|
|
| 'workspace-changelog'
|
|
| 'cache'
|
|
| 'remote'
|
|
| 'embedded-whats-new'
|
|
| 'embedded-changelog'
|
|
| 'empty';
|
|
|
|
export interface ResolvedReleaseNotes {
|
|
source: ReleaseNotesSource;
|
|
entry: ReleaseNotesEntry | null;
|
|
}
|
|
|
|
async function loadWorkspaceWhatsNewRaw(): Promise<string> {
|
|
if (import.meta.env.DEV) {
|
|
return (await import('../../../WHATS_NEW.md?raw')).default;
|
|
}
|
|
const { WHATS_NEW_RAW } = await import('../../generated/releaseNotesBundle');
|
|
return WHATS_NEW_RAW;
|
|
}
|
|
|
|
async function loadWorkspaceChangelogRaw(): Promise<string> {
|
|
if (import.meta.env.DEV) {
|
|
return (await import('../../../CHANGELOG.md?raw')).default;
|
|
}
|
|
const { CHANGELOG_RAW } = await import('../../generated/releaseNotesBundle');
|
|
return CHANGELOG_RAW;
|
|
}
|
|
|
|
async function loadEmbeddedWhatsNewRaw(): Promise<string> {
|
|
const { WHATS_NEW_RAW } = await import('../../generated/releaseNotesBundle');
|
|
return WHATS_NEW_RAW;
|
|
}
|
|
|
|
async function loadEmbeddedChangelogRaw(): Promise<string> {
|
|
const { CHANGELOG_RAW } = await import('../../generated/releaseNotesBundle');
|
|
return CHANGELOG_RAW;
|
|
}
|
|
|
|
function mergeBodyWithMeta(body: string, meta: ReleaseNotesEntry | null, version: string): ReleaseNotesEntry {
|
|
return {
|
|
body,
|
|
date: meta?.date ?? '',
|
|
headerVersion: meta?.headerVersion ?? version,
|
|
};
|
|
}
|
|
|
|
async function resolveWorkspace(version: string): Promise<ResolvedReleaseNotes> {
|
|
const whatsNewRaw = await loadWorkspaceWhatsNewRaw();
|
|
const fromWhatsNew = findReleaseNotesEntry(whatsNewRaw, version);
|
|
if (fromWhatsNew?.body) {
|
|
return { source: 'workspace', entry: fromWhatsNew };
|
|
}
|
|
|
|
const changelogRaw = await loadWorkspaceChangelogRaw();
|
|
const fromChangelog = findReleaseNotesEntry(changelogRaw, version);
|
|
if (fromChangelog?.body) {
|
|
return { source: 'workspace-changelog', entry: fromChangelog };
|
|
}
|
|
|
|
return { source: 'empty', entry: null };
|
|
}
|
|
|
|
async function resolveShipped(version: string): Promise<ResolvedReleaseNotes> {
|
|
const embeddedMeta = findReleaseNotesEntry(await loadEmbeddedWhatsNewRaw(), version);
|
|
|
|
const cached = await readReleaseNotesCache(version);
|
|
if (cached) {
|
|
return {
|
|
source: 'cache',
|
|
entry: mergeBodyWithMeta(cached, embeddedMeta, version),
|
|
};
|
|
}
|
|
|
|
const remote = await fetchWhatsNewAsset(version);
|
|
if (remote) {
|
|
await writeReleaseNotesCache(version, remote);
|
|
return {
|
|
source: 'remote',
|
|
entry: mergeBodyWithMeta(remote, embeddedMeta, version),
|
|
};
|
|
}
|
|
|
|
const fromEmbeddedWhatsNew = findReleaseNotesEntry(await loadEmbeddedWhatsNewRaw(), version);
|
|
if (fromEmbeddedWhatsNew?.body) {
|
|
return { source: 'embedded-whats-new', entry: fromEmbeddedWhatsNew };
|
|
}
|
|
|
|
const fromEmbeddedChangelog = findReleaseNotesEntry(await loadEmbeddedChangelogRaw(), version);
|
|
if (fromEmbeddedChangelog?.body) {
|
|
return { source: 'embedded-changelog', entry: fromEmbeddedChangelog };
|
|
}
|
|
|
|
return { source: 'empty', entry: null };
|
|
}
|
|
|
|
export async function resolveReleaseNotes(version: string): Promise<ResolvedReleaseNotes> {
|
|
if (isWorkspaceReleaseNotesMode(version)) {
|
|
return resolveWorkspace(version);
|
|
}
|
|
return resolveShipped(version);
|
|
}
|
|
|
|
/** Technical changelog for the same version (embedded or workspace; never remote). */
|
|
export async function resolveChangelogEntry(version: string): Promise<ResolvedReleaseNotes> {
|
|
const changelogRaw = isWorkspaceReleaseNotesMode(version)
|
|
? await loadWorkspaceChangelogRaw()
|
|
: await loadEmbeddedChangelogRaw();
|
|
const entry = findReleaseNotesEntry(changelogRaw, version);
|
|
if (entry?.body) {
|
|
return {
|
|
source: isWorkspaceReleaseNotesMode(version) ? 'workspace-changelog' : 'embedded-changelog',
|
|
entry,
|
|
};
|
|
}
|
|
return { source: 'empty', entry: null };
|
|
}
|