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
6 changes: 3 additions & 3 deletions scripts/audit-supabase-queries.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
// Usage: node scripts/audit-supabase-queries.mjs (exit 1 on any finding)

import { readFileSync, readdirSync, statSync } from 'node:fs';
import { dirname, extname, join, relative, resolve } from 'node:path';
import { dirname, extname, join, relative, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url';

const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
Expand Down Expand Up @@ -160,7 +160,7 @@ function readApiRoutes() {
const path = join(dir, entry);
if (statSync(path).isDirectory()) walk(path);
else if (/^route\.(ts|tsx|js)$/.test(entry)) {
routes.push(`/${relative(join(ROOT, 'app'), dir).split('/').join('/')}`);
routes.push(`/${relative(join(ROOT, 'app'), dir).split(sep).join('/')}`);
}
}
})(join(ROOT, 'app', 'api'));
Expand Down Expand Up @@ -324,7 +324,7 @@ export function auditSupabaseQueries() {

for (const file of listSourceFiles()) {
const src = readFileSync(file, 'utf8');
const rel = relative(ROOT, file);
const rel = relative(ROOT, file).split(sep).join('/');
const lineAt = (index) => src.slice(0, index).split('\n').length;

const fromCall = /\.from\(\s*'([a-z0-9_]+)'\s*\)/g;
Expand Down
5 changes: 3 additions & 2 deletions tests/household-trail.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// lands in both trails — lives alongside it once the service records them.
import { readFileSync } from 'node:fs';
import { globSync } from 'node:fs';
import { join } from 'node:path';
import { join, sep } from 'node:path';
import { describe, expect, it } from 'vitest';
import { describeTrail, describeTrailRow, type TrailRow } from '@/lib/activity/trail';
import { trailActionFor } from '@/lib/ai/tools/execute';
Expand Down Expand Up @@ -134,7 +134,8 @@ describe('the trail has exactly one writer', () => {

it('routes every household-trail write through lib/services/activity', () => {
const writers = globSync('lib/services/**/index.ts')
.filter((f) => /from\('audit_logs'\)\s*\.insert/.test(code(repo(f))));
.filter((f) => /from\('audit_logs'\)\s*\.insert/.test(code(repo(f))))
.map((f) => f.split(sep).join('/'));
expect(writers).toEqual(['lib/services/activity/index.ts']);
});

Expand Down
6 changes: 3 additions & 3 deletions tests/i18n-server-boundary.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';
import { readFileSync, readdirSync, statSync } from 'node:fs';
import { dirname, join, relative, resolve } from 'node:path';
import { basename, dirname, join, relative, resolve } from 'node:path';

// `lib/i18n/server.ts` imports `next/headers`. A CLIENT module that reaches it
// fails `next build` with "You're importing a component that needs next/headers"
Expand Down Expand Up @@ -189,13 +189,13 @@ describe('lib/i18n/server never reaches the browser', () => {
* entry and STOPS at the first `'use client'`. A module the server can still
* reach when it stops may not call a hook.
*/
const ROUTE_ENTRY = /(^|\/)(page|layout|template|default|not-found|loading|route)\.tsx?$/;
const ROUTE_ENTRY = /^(page|layout|template|default|not-found|loading|route)\.tsx?$/;

function serverRenderedModules(): Set<string> {
const roots = files.filter(
(file) =>
file.startsWith(join(ROOT, 'app'))
&& ROUTE_ENTRY.test(file)
&& ROUTE_ENTRY.test(basename(file))
&& !sources.get(file)!.declaresClient,
);
const reached = new Set(roots);
Expand Down
16 changes: 10 additions & 6 deletions tests/metadata-title-not-catalogue-key.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';
import { readFileSync } from 'node:fs';
import { execSync } from 'node:child_process';
import { readFileSync, readdirSync } from 'node:fs';
import { join } from 'node:path';

// A Next.js `metadata` export is static: it cannot await `getTranslations()`, so
// whatever string sits in `title` is rendered VERBATIM into <title>. A catalogue
Expand All @@ -12,15 +12,19 @@ import { execSync } from 'node:child_process';
// nor rendered through `t()`. So it needs its own guard.
const CATALOGUE_KEY = /^[a-z][a-zA-Z0-9]*\.[a-zA-Z][a-zA-Z0-9]*$/;

function pageFiles(): string[] {
return execSync("find app -name 'page.tsx' -o -name 'layout.tsx'", { encoding: 'utf8' })
.split('\n').filter(Boolean);
function pageFiles(directory = 'app'): string[] {
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
const path = join(directory, entry.name);
return entry.isDirectory() ? pageFiles(path) : ['page.tsx', 'layout.tsx'].includes(entry.name) ? [path] : [];
});
}

describe('metadata titles are real text, never catalogue keys', () => {
it('has no page shipping a catalogue key as its browser-tab title', () => {
const offenders: string[] = [];
for (const file of pageFiles()) {
const files = pageFiles();
expect(files.length).toBeGreaterThan(100);
for (const file of files) {
const source = readFileSync(file, 'utf8');
for (const m of source.matchAll(/export const metadata: Metadata = \{[^}]*title: '([^']+)'/g)) {
if (CATALOGUE_KEY.test(m[1])) offenders.push(`${file}: ${m[1]}`);
Expand Down
5 changes: 3 additions & 2 deletions tests/untrusted-fence-ratchet.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { readFileSync, readdirSync, statSync } from 'node:fs';
import { join } from 'node:path';
import { join, sep } from 'node:path';
import { describe, expect, it } from 'vitest';

// §44's fence is genuinely well built — nonce markers, a rule the model is told
Expand Down Expand Up @@ -45,7 +45,8 @@ function toolCallingModules(): string[] {
const src = readFileSync(path, 'utf8');
if (!/resolveProvider|provider\.(complete|runTools)/.test(src)) return false;
return /tools:\s*(?!\[\s*\])[A-Za-z_[]/.test(src);
});
})
.map((path) => path.split(sep).join('/'));
}

describe('the untrusted-content fence reaches every tool-calling prompt', () => {
Expand Down
Loading