diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..e58bbc2c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: CI + +on: + pull_request: + paths-ignore: + - '**/*.md' + - 'docs/**' + push: + branches: [main] + paths-ignore: + - '**/*.md' + - 'docs/**' + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + quality: + name: Typecheck + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Typecheck + run: pnpm typecheck + + unit: + name: Unit tests + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Web tests + run: pnpm test:web + + - name: CLI tests + run: pnpm test:cli diff --git a/src/app/api/macro/flat/data/route.ts b/src/app/api/macro/flat/data/route.ts index e3a0da0e..ab69f781 100644 --- a/src/app/api/macro/flat/data/route.ts +++ b/src/app/api/macro/flat/data/route.ts @@ -5,7 +5,8 @@ export async function GET() { const buf = await getMacroFlatData(); if (!buf) return new NextResponse('Not found', { status: 404 }); // Cache binary strongly; it only changes when prep runs - return new NextResponse(buf, { status: 200, headers: { 'Content-Type': 'application/octet-stream', 'Cache-Control': 'public, max-age=86400, immutable' } }); + return new NextResponse( + new Blob([Uint8Array.from(buf)]), + { status: 200, headers: { 'Content-Type': 'application/octet-stream', 'Cache-Control': 'public, max-age=86400, immutable' } }, + ); } - - diff --git a/src/app/api/macro/flat/models/[modelId]/data/route.ts b/src/app/api/macro/flat/models/[modelId]/data/route.ts index 8538a6b8..fc50dc56 100644 --- a/src/app/api/macro/flat/models/[modelId]/data/route.ts +++ b/src/app/api/macro/flat/models/[modelId]/data/route.ts @@ -5,7 +5,8 @@ export async function GET(_: Request, ctx: { params: Promise<{ modelId: string } const params = await ctx.params; const buf = await getMacroPerModelData(params.modelId); if (!buf) return new NextResponse('Not found', { status: 404 }); - return new NextResponse(buf, { status: 200, headers: { 'Content-Type': 'application/octet-stream', 'Cache-Control': 'public, max-age=86400, immutable' } }); + return new NextResponse( + new Blob([Uint8Array.from(buf)]), + { status: 200, headers: { 'Content-Type': 'application/octet-stream', 'Cache-Control': 'public, max-age=86400, immutable' } }, + ); } - - diff --git a/src/app/api/macro/tiles/[z]/[x]/[y]/route.ts b/src/app/api/macro/tiles/[z]/[x]/[y]/route.ts index 8dc885e0..0b5ba89f 100644 --- a/src/app/api/macro/tiles/[z]/[x]/[y]/route.ts +++ b/src/app/api/macro/tiles/[z]/[x]/[y]/route.ts @@ -8,7 +8,8 @@ export async function GET(_: Request, ctx: { params: Promise<{ z: string; x: str const y = parseInt(params.y, 10); const buf = await getMacroTile(z, x, y); if (!buf) return new NextResponse('Not found', { status: 404 }); - return new NextResponse(buf, { status: 200, headers: { 'Content-Type': 'application/octet-stream', 'Cache-Control': 'public, max-age=31536000, immutable' } }); + return new NextResponse( + new Blob([Uint8Array.from(buf)]), + { status: 200, headers: { 'Content-Type': 'application/octet-stream', 'Cache-Control': 'public, max-age=31536000, immutable' } }, + ); } - - diff --git a/src/app/api/sandbox/auto-wiki/route.ts b/src/app/api/sandbox/auto-wiki/route.ts index 5dd80aad..7404a39c 100644 --- a/src/app/api/sandbox/auto-wiki/route.ts +++ b/src/app/api/sandbox/auto-wiki/route.ts @@ -150,13 +150,14 @@ export async function POST(req: NextRequest) { } // wtf.fetch can return an array if it follows redirects, take the last one. - if (Array.isArray(doc)) { - doc = doc[doc.length - 1]; + const article = Array.isArray(doc) ? doc[doc.length - 1] : doc; + if (!article) { + return NextResponse.json({ error: 'Could not resolve the Wikipedia article.' }, { status: 404 }); } - const articleText = doc.text(); - const articleSummary = (doc as any).summary() || 'No summary available.'; - const title = doc.title() || articleTitle; + const articleText = article.text(); + const articleSummary = (article as any).summary() || 'No summary available.'; + const title = article.title() || articleTitle; if (!articleText || articleText.length < 100) { return NextResponse.json({ error: 'The article content is too short to generate a meaningful blueprint.' }, { status: 400 }); @@ -200,4 +201,4 @@ export async function POST(req: NextRequest) { console.error('[Auto-Wiki Error]', error); return NextResponse.json({ error: 'An unexpected error occurred.', details: error.message }, { status: 500 }); } -} \ No newline at end of file +}