Add multi-platform function adapters and CI workflow
Introduces adapters for Vercel, Netlify Edge, Deno Deploy, and Cloudflare Pages, each with platform-specific entry points and configuration files. Adds a GitHub Actions CI workflow for linting, type checking, and testing. Updates lint scripts to include adapters, improves type annotations in src/index.js, and fixes a type assertion in security tests.
This commit is contained in:
1 parent
214e596d00
commit
44b54580ad
13 files changed
+364
-348
No files matched your search
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Xget - High-performance acceleration engine for developer resources
|
||||
* Copyright (C) 2025 Xi Xu
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-undef, no-unused-vars */
|
||||
|
||||
import { handleRequest } from '../src/index.js';
|
||||
|
||||
/**
|
||||
* Edge Function handler.
|
||||
*
|
||||
* @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<Response>} Standard Web API Response
|
||||
*
|
||||
* @example
|
||||
* // Netlify invokes with context
|
||||
* handler(request, { geo: {...}, ip: '1.2.3.4', env: {...}, waitUntil: fn })
|
||||
*
|
||||
* @example
|
||||
* // Vercel invokes without context
|
||||
* handler(request)
|
||||
*/
|
||||
export default async function handler(request, context) {
|
||||
// Detect runtime environment
|
||||
const isNetlify = context && (context.geo !== undefined || context.ip !== undefined);
|
||||
|
||||
// Normalize environment variables
|
||||
// Netlify provides context.env, Vercel Edge uses globalThis
|
||||
let envSource;
|
||||
if (isNetlify) {
|
||||
envSource = context.env || {};
|
||||
} else if (typeof process !== 'undefined' && process.env) {
|
||||
// Vercel or Node.js environment
|
||||
envSource = process.env;
|
||||
} else {
|
||||
// Fallback for other environments
|
||||
envSource = {};
|
||||
}
|
||||
|
||||
const env = {
|
||||
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 normalized execution context
|
||||
const ctx = {
|
||||
waitUntil: isNetlify && context.waitUntil
|
||||
? (promise) => context.waitUntil(promise)
|
||||
: (promise) => {
|
||||
// No-op on Vercel: background tasks not supported
|
||||
// Cache writes will run synchronously instead
|
||||
console.warn('waitUntil is not supported in Vercel Edge Runtime');
|
||||
},
|
||||
passThroughOnException: () => {
|
||||
// Not supported on either platform in this context
|
||||
console.warn('passThroughOnException is not universally supported');
|
||||
}
|
||||
};
|
||||
|
||||
// Delegate to the main request handler
|
||||
return handleRequest(request, env, ctx);
|
||||
}
|
||||
|
||||
// Vercel Edge Runtime configuration (ignored by Netlify)
|
||||
export const config = {
|
||||
runtime: 'edge',
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Xget - High-performance acceleration engine for developer resources
|
||||
* Copyright (C) 2025 Xi Xu
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-undef, no-unused-vars */
|
||||
|
||||
import { handleRequest } from './src/index.js';
|
||||
|
||||
/**
|
||||
* Deno Deploy handler.
|
||||
*
|
||||
* This is the entry point for Deno Deploy deployments. It uses the
|
||||
* standard Deno.serve() API to handle incoming HTTP requests.
|
||||
*
|
||||
* @param {Request} request - Standard Web API Request object
|
||||
* @returns {Promise<Response>} Standard Web API Response
|
||||
*
|
||||
* @example
|
||||
* // Deno Deploy invokes automatically:
|
||||
* // Deno.serve((request) => handler(request))
|
||||
*/
|
||||
async function handler(request) {
|
||||
// Extract environment variables from Deno.env
|
||||
const env = {
|
||||
TIMEOUT_SECONDS: Deno.env.get('TIMEOUT_SECONDS'),
|
||||
MAX_RETRIES: Deno.env.get('MAX_RETRIES'),
|
||||
RETRY_DELAY_MS: Deno.env.get('RETRY_DELAY_MS'),
|
||||
CACHE_DURATION: Deno.env.get('CACHE_DURATION'),
|
||||
ALLOWED_METHODS: Deno.env.get('ALLOWED_METHODS'),
|
||||
ALLOWED_ORIGINS: Deno.env.get('ALLOWED_ORIGINS'),
|
||||
MAX_PATH_LENGTH: Deno.env.get('MAX_PATH_LENGTH'),
|
||||
};
|
||||
|
||||
// Create minimal ExecutionContext-like object
|
||||
// Deno Deploy doesn't support waitUntil, so cache writes are synchronous
|
||||
const ctx = {
|
||||
waitUntil: (promise) => {
|
||||
// No-op on Deno: background tasks not supported
|
||||
console.warn('waitUntil is not supported in Deno Deploy');
|
||||
},
|
||||
passThroughOnException: () => {
|
||||
console.warn('passThroughOnException is not supported in Deno Deploy');
|
||||
}
|
||||
};
|
||||
|
||||
// Delegate to the main request handler
|
||||
return handleRequest(request, env, ctx);
|
||||
}
|
||||
|
||||
// Start the server
|
||||
Deno.serve(handler);
|
||||
@@ -0,0 +1,6 @@
|
||||
[[edge_functions]]
|
||||
function = "edge-handler"
|
||||
path = "/*"
|
||||
|
||||
[build]
|
||||
publish = "."
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 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 { config, default } from '../../api/index.js';
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "xget",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"dev": "vercel dev",
|
||||
"deploy": "vercel --prod",
|
||||
"vercel-build": "echo 'No build step required'"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.19.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vercel/node": "^3.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||
"name": "xget",
|
||||
"version": 2,
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/(.*)",
|
||||
"destination": "/api/index.js"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"source": "/(.*)",
|
||||
"headers": [
|
||||
{
|
||||
"key": "X-Powered-By",
|
||||
"value": "Xget/Vercel"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Xget - High-performance acceleration engine for developer resources
|
||||
* Copyright (C) 2025 Xi Xu
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
import { handleRequest } from '../src/index.js';
|
||||
|
||||
/**
|
||||
* Pages Function handler for all routes.
|
||||
*
|
||||
* This catch-all route handler processes all incoming requests to the Xget
|
||||
* acceleration engine. It delegates request processing to the main handleRequest
|
||||
* function from the Workers code, maintaining full compatibility with the
|
||||
* existing implementation.
|
||||
*
|
||||
* The [[path]] syntax in the filename creates a catch-all route that matches
|
||||
* any path, allowing this single function to handle all requests to the Pages
|
||||
* application.
|
||||
*
|
||||
* @param {Object} context - Pages Function context
|
||||
* @param {Request} context.request - The incoming HTTP request
|
||||
* @param {Object} context.env - Environment variables and bindings (KV, secrets, etc.)
|
||||
* @param {Object} context.params - Route parameters (path segments from [[path]])
|
||||
* @param {Function} context.waitUntil - Extend function execution for background tasks
|
||||
* @param {Function} context.next - Call next middleware in chain (not used here)
|
||||
* @param {Object} context.data - Shared data between functions
|
||||
* @returns {Promise<Response>} The HTTP response to return to the client
|
||||
*
|
||||
* @example
|
||||
* // This is called automatically by Pages
|
||||
* // Runtime invokes: onRequest(context)
|
||||
* // Returns: Response with package data
|
||||
*
|
||||
* @example
|
||||
* // Environment variables usage
|
||||
* // wrangler.toml: [vars] TIMEOUT_SECONDS = "60"
|
||||
* // context.env contains: { TIMEOUT_SECONDS: "60" }
|
||||
* // handleRequest uses createConfig(env) to override defaults
|
||||
*/
|
||||
export async function onRequest(context) {
|
||||
// Extract request, env, and create an execution context compatible with Workers
|
||||
const { request, env, waitUntil } = context;
|
||||
|
||||
// Create a minimal ExecutionContext-like object for compatibility
|
||||
const ctx = {
|
||||
waitUntil: waitUntil,
|
||||
passThroughOnException: () => {
|
||||
// Pages doesn't support passThroughOnException, so this is a no-op
|
||||
console.warn('passThroughOnException is not supported in Pages Functions');
|
||||
}
|
||||
};
|
||||
|
||||
// Delegate to the main request handler
|
||||
return handleRequest(request, env, ctx);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#:schema node_modules/wrangler/config-schema.json
|
||||
name = "xget"
|
||||
pages_build_output_dir = "."
|
||||
compatibility_date = "2024-10-22"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
Reference in new issue
Block a user