Files
worldmonitor/tests/edge-functions.test.mjs
Elie Habib 898ac7b1c4 perf(rss): route RSS direct to Railway, skip Vercel middleman (#961)
* perf(rss): route RSS direct to Railway, skip Vercel middleman

Vercel /api/rss-proxy has 65% error rate (207K failed invocations/12h).
Route browser RSS requests directly to Railway (proxy.worldmonitor.app)
via Cloudflare CDN, eliminating Vercel as middleman.

- Add VITE_RSS_DIRECT_TO_RELAY feature flag (default off) for staged rollout
- Centralize RSS proxy URL in rssProxyUrl() with desktop/dev/prod routing
- Make Railway /rss public (skip auth, keep rate limiting with CF-Connecting-IP)
- Add wildcard *.worldmonitor.app CORS + always emit Vary: Origin on /rss
- Extract ~290 RSS domains to shared/rss-allowed-domains.cjs (single source of truth)
- Convert Railway domain check to Set for O(1) lookups
- Remove rss-proxy from KEYED_CLOUD_API_PATTERN (no longer needs API key header)
- Add edge function test for shared domain list import

* fix(edge): replace node:module with JSON import for edge-compatible RSS domains

api/_rss-allowed-domains.js used createRequire from node:module which is
unsupported in Vercel Edge Runtime, breaking all edge functions (including
api/gpsjam). Replaced with JSON import attribute syntax that works in both
esbuild (Vercel build) and Node.js 22+ (tests).

Also fixed middleware.ts TS18048 error where VARIANT_OG[variant] could be
undefined.

* test(edge): add guard against node: built-in imports in api/ files

Scans ALL api/*.js files (including _ helpers) for node: module imports
which are unsupported in Vercel Edge Runtime. This would have caught the
createRequire(node:module) bug before it reached Vercel.

* fix(edge): inline domain array and remove NextResponse reference

- Replace `import ... with { type: 'json' }` in _rss-allowed-domains.js
  with inline array — Vercel esbuild doesn't support import attributes
- Replace `NextResponse.next()` with bare `return` in middleware.ts —
  NextResponse was never imported

* ci(pre-push): add esbuild bundle check and edge function tests

The pre-push hook now catches Vercel build failures locally:
- esbuild bundles each api/*.js entrypoint (catches import attribute
  syntax, missing modules, and other bundler errors)
- runs edge function test suite (node: imports, module isolation)
2026-03-04 18:42:00 +04:00

63 lines
2.6 KiB
JavaScript

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync, readdirSync } from 'node:fs';
import { dirname, resolve, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, '..');
const apiDir = join(root, 'api');
// All .js files in api/ except underscore-prefixed helpers (_cors.js, _api-key.js)
const edgeFunctions = readdirSync(apiDir)
.filter((f) => f.endsWith('.js') && !f.startsWith('_'))
.map((f) => ({ name: f, path: join(apiDir, f) }));
// ALL .js files in api/ (including helpers) — used for node: built-in checks
const allApiFiles = readdirSync(apiDir)
.filter((f) => f.endsWith('.js'))
.map((f) => ({ name: f, path: join(apiDir, f) }));
describe('Edge Function shared helpers resolve', () => {
it('_rss-allowed-domains.js re-exports shared domain list', async () => {
const mod = await import(join(apiDir, '_rss-allowed-domains.js'));
const domains = mod.default;
assert.ok(Array.isArray(domains), 'Expected default export to be an array');
assert.ok(domains.length > 200, `Expected 200+ domains, got ${domains.length}`);
assert.ok(domains.includes('feeds.bbci.co.uk'), 'Expected BBC feed domain in list');
});
});
describe('Edge Function no node: built-ins', () => {
for (const { name, path } of allApiFiles) {
it(`${name} does not import node: built-ins (unsupported in Vercel Edge Runtime)`, () => {
const src = readFileSync(path, 'utf-8');
const match = src.match(/from\s+['"]node:(\w+)['"]/);
assert.ok(
!match,
`${name}: imports node:${match?.[1]} — Vercel Edge Runtime does not support node: built-in modules. Use an edge-compatible alternative.`,
);
});
}
});
describe('Edge Function module isolation', () => {
for (const { name, path } of edgeFunctions) {
it(`${name} does not import from ../server/ (Edge Functions cannot resolve cross-directory TS)`, () => {
const src = readFileSync(path, 'utf-8');
assert.ok(
!src.includes("from '../server/"),
`${name}: imports from ../server/ — Vercel Edge Functions cannot resolve cross-directory TS imports. Inline the code or move to a same-directory .js helper.`,
);
});
it(`${name} does not import from ../src/ (Edge Functions cannot resolve TS aliases)`, () => {
const src = readFileSync(path, 'utf-8');
assert.ok(
!src.includes("from '../src/"),
`${name}: imports from ../src/ — Vercel Edge Functions cannot resolve @/ aliases or cross-directory TS. Inline the code instead.`,
);
});
}
});