diff --git a/.github/workflows/functions-ntl.yml b/.github/workflows/functions-ntl.yml new file mode 100644 index 0000000..18ec62e --- /dev/null +++ b/.github/workflows/functions-ntl.yml @@ -0,0 +1,41 @@ +name: Deploy to Netlify + +on: + workflow_run: + workflows: ["Sync to Pages and Functions"] + types: + - completed + branches: + - main + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read + deployments: write + if: ${{ github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Checkout functions branch + uses: actions/checkout@v6 + with: + ref: functions + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "20" + cache: "npm" + + - name: Install Netlify CLI + run: npm install --global netlify-cli@latest + + - name: Deploy to Netlify + run: netlify deploy --prod --dir=. --auth=${{ secrets.NETLIFY_AUTH_TOKEN }} --site=${{ secrets.NETLIFY_SITE_ID }} diff --git a/.github/workflows/functions-vc.yml b/.github/workflows/functions-vc.yml index 57b55b0..07bdcc2 100644 --- a/.github/workflows/functions-vc.yml +++ b/.github/workflows/functions-vc.yml @@ -1,4 +1,4 @@ -name: Deploy to Vercel Functions +name: Deploy to Vercel on: workflow_run: diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index ec400ee..35cdf56 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -212,7 +212,7 @@ jobs: cp package-lock.json /tmp/functions-conversion/ 2>/dev/null || true cp LICENSE /tmp/functions-conversion/ - - name: Create Vercel Edge Function handler + - name: Create Edge Function handler run: | mkdir -p /tmp/functions-conversion/api cat > /tmp/functions-conversion/api/index.js << 'EOF' @@ -237,51 +237,56 @@ jobs: import { handleRequest } from '../src/index.js'; /** - * Vercel Edge Function handler for all routes. + * Edge Function handler. * - * This adapter enables Xget to run on Vercel's Edge Runtime by mapping - * Vercel's environment model to Cloudflare Workers' expected format. - * - * The Vercel Edge Runtime uses standard Web APIs (Request, Response, fetch), - * making it compatible with the existing Cloudflare Workers code. - * - * @param {Request} request - Standard Web API Request + * @param {Request} request - Standard Web API Request object + * @param {Object} [context] - Platform-specific context (Netlify only) + * @param {Object} [context.geo] - Geolocation data (Netlify) + * @param {string} [context.ip] - Client IP address (Netlify) + * @param {Object} [context.env] - Environment variables (Netlify) + * @param {Function} [context.waitUntil] - Background task extension (Netlify) * @returns {Promise} Standard Web API Response * * @example - * // This is called automatically by Vercel Edge Runtime - * // Runtime invokes: handler(request) - * // Returns: Response with proxied content + * // Netlify invokes with context + * handler(request, { geo: {...}, ip: '1.2.3.4', env: {...}, waitUntil: fn }) * * @example - * // Environment variables usage - * // Vercel dashboard or vercel.json: TIMEOUT_SECONDS = "60" - * // process.env contains: { TIMEOUT_SECONDS: "60" } - * // Mapped to env object and passed to handleRequest + * // Vercel invokes without context + * handler(request) */ - export default async function handler(request) { - // Map process.env to Cloudflare Workers env format + export default async function handler(request, context) { + // Detect runtime environment + const isNetlify = context && (context.geo !== undefined || context.ip !== undefined); + + // Normalize environment variables + const envSource = isNetlify && context.env ? context.env : process.env; const env = { - TIMEOUT_SECONDS: process.env.TIMEOUT_SECONDS, - MAX_RETRIES: process.env.MAX_RETRIES, - RETRY_DELAY_MS: process.env.RETRY_DELAY_MS, - CACHE_DURATION: process.env.CACHE_DURATION, - ALLOWED_METHODS: process.env.ALLOWED_METHODS, - ALLOWED_ORIGINS: process.env.ALLOWED_ORIGINS, - MAX_PATH_LENGTH: process.env.MAX_PATH_LENGTH, + TIMEOUT_SECONDS: envSource.TIMEOUT_SECONDS, + MAX_RETRIES: envSource.MAX_RETRIES, + RETRY_DELAY_MS: envSource.RETRY_DELAY_MS, + CACHE_DURATION: envSource.CACHE_DURATION, + ALLOWED_METHODS: envSource.ALLOWED_METHODS, + ALLOWED_ORIGINS: envSource.ALLOWED_ORIGINS, + MAX_PATH_LENGTH: envSource.MAX_PATH_LENGTH, }; - // Create minimal execution context - // Note: Vercel Edge Runtime doesn't support waitUntil or passThroughOnException + // Create normalized execution context const ctx = { - waitUntil: (promise) => { - // No-op: Vercel doesn't support background tasks - // Cache writes will run synchronously instead - console.warn('waitUntil is not supported in Vercel Edge Runtime'); - }, + waitUntil: isNetlify && context.waitUntil + ? (promise) => context.waitUntil(promise) + : (promise) => { + // No-op on Vercel: background tasks not supported + // Cache writes will run synchronously instead + if (process.env.NODE_ENV !== 'production') { + console.warn('waitUntil is not supported in Vercel Edge Runtime'); + } + }, passThroughOnException: () => { - // No-op: Vercel-specific error handling - console.warn('passThroughOnException is not supported in Vercel Edge Runtime'); + // Not supported on either platform in this context + if (process.env.NODE_ENV !== 'production') { + console.warn('passThroughOnException is not universally supported'); + } } }; @@ -289,7 +294,7 @@ jobs: return handleRequest(request, env, ctx); } - // Vercel Edge Runtime configuration + // Vercel Edge Runtime configuration (ignored by Netlify) export const config = { runtime: 'edge', }; @@ -323,6 +328,37 @@ jobs: } EOF + - name: Create netlify.toml + run: | + cd /tmp/functions-conversion + cat > netlify.toml << 'EOF' + [[edge_functions]] + function = "edge-handler" + path = "/*" + + [build] + publish = "." + EOF + + - name: Create Netlify Edge Function redirect + run: | + cd /tmp/functions-conversion + mkdir -p netlify/edge-functions + cat > netlify/edge-functions/edge-handler.js << 'EOF' + /** + * Netlify Edge Function entry point. + * + * This file serves as a redirect to the edge function handler + * located at /api/index.js. Both Netlify and Vercel can use the same + * handler code, with platform detection handling the differences. + * + * Netlify requires edge functions to be in netlify/edge-functions/, + * while Vercel uses /api/ directory. This approach maintains a single + * source of truth at /api/index.js. + */ + export { default, config } from '../../api/index.js'; + EOF + - name: Export handleRequest from src/index.js run: | cd /tmp/functions-conversion