release: v0.16.0

See CHANGELOG.md for the full v0.16.0 entry.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ralf <rohlenbo79@gmail.com>
Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com>
Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com>
This commit is contained in:
Ralksta
2026-09-22 14:12:13 +02:00
committed by GitHub
parent c3f92ca4ec
commit a3a6319c29
120 changed files with 6818 additions and 975 deletions

View File

@@ -13,6 +13,7 @@ vi.mock('@/lib/auth', () => ({
}));
import { isSiteUnlocked } from '@/lib/auth';
import { tryToParsePath } from 'next/dist/lib/try-to-parse-path';
import { proxy, config } from '@/proxy';
const mockUnlocked = isSiteUnlocked as unknown as ReturnType<typeof vi.fn>;
@@ -88,6 +89,53 @@ describe('proxy', () => {
expect(source).not.toContain('admin');
});
/**
* Reading the pattern is not enough: whether a path reaches proxy() is
* decided by the regex Next compiles from it, so that is what gets tested.
* A path the matcher skips gets no site gate and no CSP.
*/
describe('matcher, compiled the way Next compiles it', () => {
const compiled = tryToParsePath(config.matcher[0]);
const reaches = (pathname: string) => new RegExp(compiled.regexStr!).test(pathname);
it('compiles', () => {
expect(compiled.error).toBeUndefined();
expect(compiled.regexStr).toBeTruthy();
});
it.each(['/', '/journal', '/japan/kyoto', '/admin', '/admin/pages', '/gate', '/install'])(
'runs on %s',
(pathname) => expect(reaches(pathname)).toBe(true),
);
it.each([
'/api',
'/api/image/v2:abc',
'/_next/static/chunks/main.js',
'/_next/image',
'/favicon.ico',
'/sitemap.xml',
'/robots.txt',
])('skips %s', (pathname) => expect(reaches(pathname)).toBe(false));
// An exclusion is a whole segment or a whole filename, never a prefix: a
// page whose slug happens to start with one must still be gated.
it.each([
'/apia-samoa',
'/apiary',
'/api-docs',
'/_next/staticfoo',
'/_next/imagery',
'/sitemapXxml',
'/robots1txt',
'/faviconXico',
'/robots.txt.bak',
'/sitemap.xml/extra',
])('runs on %s, which only looks like an exclusion', (pathname) =>
expect(reaches(pathname)).toBe(true),
);
});
/**
* The gate resolves the site password through getConfig(), which throws on a
* config it cannot parse — inside the proxy, before app/layout.tsx can fall