refactor(core): modularize request pipeline
This commit is contained in:
1 parent
b86be36d67
commit
1b67fd2ff1
36 files changed
+2047
-1271
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { PLATFORMS, transformPath } from '../../src/config/platforms.js';
|
||||
import { PLATFORM_CATALOG as PLATFORMS } from '../../src/config/platform-catalog.js';
|
||||
import { transformPath } from '../../src/routing/platform-transformers.js';
|
||||
|
||||
describe('CRAN Platform Configuration', () => {
|
||||
it('should have CRAN platform configured', () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { transformPath } from '../../src/config/platforms.js';
|
||||
import { transformPath } from '../../src/routing/platform-transformers.js';
|
||||
|
||||
describe('crates.io path transformation', () => {
|
||||
it('should transform crate download URLs correctly', () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { PLATFORMS, transformPath } from '../../src/config/platforms.js';
|
||||
import { PLATFORM_CATALOG as PLATFORMS } from '../../src/config/platform-catalog.js';
|
||||
import { transformPath } from '../../src/routing/platform-transformers.js';
|
||||
|
||||
describe('Flathub Platform Configuration', () => {
|
||||
it('should have Flathub platform configured', () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { transformPath } from '../../src/config/platforms.js';
|
||||
import { transformPath } from '../../src/routing/platform-transformers.js';
|
||||
|
||||
describe('Homebrew path transformation', () => {
|
||||
describe('homebrew-api platform', () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { transformPath } from '../../src/config/platforms.js';
|
||||
import { transformPath } from '../../src/routing/platform-transformers.js';
|
||||
|
||||
describe('Jenkins Plugin Support', () => {
|
||||
describe('Update Center Transformations', () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { PLATFORMS, transformPath } from '../../src/config/platforms.js';
|
||||
import { PLATFORM_CATALOG as PLATFORMS } from '../../src/config/platform-catalog.js';
|
||||
import { transformPath } from '../../src/routing/platform-transformers.js';
|
||||
|
||||
describe('openSUSE Platform Configuration', () => {
|
||||
it('should have openSUSE platform configured', () => {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import apiHandler, { config as vercelConfig } from '../../adapters/functions/api/index.js';
|
||||
import { handler as denoHandler } from '../../adapters/functions/deno.js';
|
||||
import { onRequest } from '../../adapters/pages/functions/[[path]].js';
|
||||
import { createRequestContext } from '../../src/app/request-context.js';
|
||||
import { PLATFORM_CATALOG } from '../../src/config/platform-catalog.js';
|
||||
import { normalizeEffectivePath, resolveTarget } from '../../src/routing/resolve-target.js';
|
||||
|
||||
describe('Application structure', () => {
|
||||
it('builds a shared request context for protocol-aware routing', () => {
|
||||
const request = new Request('https://example.com/ip/openai/v1/chat/completions', {
|
||||
method: 'OPTIONS',
|
||||
headers: {
|
||||
Origin: 'https://app.example.com',
|
||||
'Access-Control-Request-Method': 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
const context = createRequestContext(request, {
|
||||
ALLOWED_METHODS: 'GET,HEAD,POST'
|
||||
});
|
||||
|
||||
expect(context.isAI).toBe(true);
|
||||
expect(context.isCorsPreflight).toBe(true);
|
||||
expect(context.config.SECURITY.ALLOWED_METHODS).toContain('POST');
|
||||
});
|
||||
|
||||
it('normalizes Docker host-style paths before resolving upstream targets', () => {
|
||||
const url = new URL('https://example.com/v2/cr/ghcr/xixu-me/xget/manifests/latest');
|
||||
const normalized = normalizeEffectivePath(url, true);
|
||||
|
||||
expect('effectivePath' in normalized).toBe(true);
|
||||
if ('effectivePath' in normalized) {
|
||||
expect(normalized.effectivePath).toBe('/cr/ghcr/v2/xixu-me/xget/manifests/latest');
|
||||
|
||||
const target = resolveTarget(url, normalized.effectivePath, PLATFORM_CATALOG);
|
||||
expect('response' in target).toBe(false);
|
||||
if (!('response' in target)) {
|
||||
expect(target.platform).toBe('cr-ghcr');
|
||||
expect(target.targetUrl).toBe('https://ghcr.io/v2/xixu-me/xget/manifests/latest');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('exposes thin runtime adapter entrypoints', () => {
|
||||
expect(typeof apiHandler).toBe('function');
|
||||
expect(typeof denoHandler).toBe('function');
|
||||
expect(typeof onRequest).toBe('function');
|
||||
expect(vercelConfig).toEqual({ runtime: 'edge' });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,116 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { createRequestContext } from '../../src/app/request-context.js';
|
||||
import { CONFIG } from '../../src/config/index.js';
|
||||
import { finalizeResponse } from '../../src/response/finalize-response.js';
|
||||
import { tryReadCachedResponse } from '../../src/upstream/cache.js';
|
||||
import { fetchUpstreamResponse } from '../../src/upstream/fetch-upstream.js';
|
||||
import { PerformanceMonitor } from '../../src/utils/performance.js';
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('Pipeline modules', () => {
|
||||
it('reuses cached full content for range requests through the cache helper', async () => {
|
||||
const cache = {
|
||||
match: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(null)
|
||||
.mockResolvedValueOnce(
|
||||
new Response('full-body', {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'text/plain' }
|
||||
})
|
||||
)
|
||||
};
|
||||
const monitor = new PerformanceMonitor();
|
||||
const markSpy = vi.spyOn(monitor, 'mark');
|
||||
const request = new Request('https://example.com/gh/user/repo/file.txt', {
|
||||
headers: { Range: 'bytes=0-3' }
|
||||
});
|
||||
|
||||
const response = await tryReadCachedResponse({
|
||||
cache: /** @type {Cache} */ (/** @type {unknown} */ (cache)),
|
||||
cacheTargetUrl: 'https://github.com/user/repo/file.txt',
|
||||
canUseCache: true,
|
||||
hasSensitiveHeaders: false,
|
||||
monitor,
|
||||
request,
|
||||
requestContext: createRequestContext(request, {})
|
||||
});
|
||||
|
||||
expect(await response?.text()).toBe('full-body');
|
||||
expect(markSpy).toHaveBeenCalledWith('cache_hit_full_content');
|
||||
});
|
||||
|
||||
it('retries upstream fetches through the transport helper before succeeding', async () => {
|
||||
const request = new Request('https://example.com/gh/user/repo/file.txt');
|
||||
const requestContext = createRequestContext(request, {});
|
||||
const fetchSpy = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockRejectedValueOnce(new Error('temporary-network-error'))
|
||||
.mockResolvedValueOnce(
|
||||
new Response('ok', {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'text/plain' }
|
||||
})
|
||||
);
|
||||
|
||||
const result = await fetchUpstreamResponse({
|
||||
authorization: null,
|
||||
canUseCache: true,
|
||||
config: { ...CONFIG, MAX_RETRIES: 2, RETRY_DELAY_MS: 0 },
|
||||
effectivePath: '/gh/user/repo/file.txt',
|
||||
monitor: new PerformanceMonitor(),
|
||||
platform: 'gh',
|
||||
request,
|
||||
requestContext,
|
||||
shouldPassthroughRequest: false,
|
||||
targetUrl: 'https://github.com/user/repo/file.txt'
|
||||
});
|
||||
|
||||
expect(result.responseGeneratedLocally).toBe(false);
|
||||
expect(result.response.status).toBe(200);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('rewrites npm metadata and refreshes content length during response finalization', async () => {
|
||||
const request = new Request('https://example.com/npm/pkg');
|
||||
const requestContext = createRequestContext(request, {});
|
||||
const upstreamBody = JSON.stringify({
|
||||
dist: {
|
||||
tarball: 'https://registry.npmjs.org/pkg/-/pkg-1.0.0.tgz'
|
||||
}
|
||||
});
|
||||
|
||||
const response = await finalizeResponse({
|
||||
cache: null,
|
||||
cacheTargetUrl: 'https://registry.npmjs.org/pkg',
|
||||
canUseCache: true,
|
||||
config: CONFIG,
|
||||
ctx: /** @type {ExecutionContext} */ ({ waitUntil() {}, passThroughOnException() {} }),
|
||||
effectivePath: '/npm/pkg',
|
||||
hasSensitiveHeaders: false,
|
||||
monitor: new PerformanceMonitor(),
|
||||
platform: 'npm',
|
||||
request,
|
||||
requestContext,
|
||||
response: new Response(upstreamBody, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': String(upstreamBody.length)
|
||||
}
|
||||
}),
|
||||
responseGeneratedLocally: false,
|
||||
url: new URL(request.url)
|
||||
});
|
||||
const body = await response.text();
|
||||
|
||||
expect(body).toContain('https://example.com/npm/pkg/-/pkg-1.0.0.tgz');
|
||||
expect(response.headers.get('Content-Length')).toBe(
|
||||
String(new TextEncoder().encode(body).byteLength)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { PLATFORM_CATALOG } from '../../src/config/platform-catalog.js';
|
||||
import { PLATFORMS, SORTED_PLATFORMS, transformPath } from '../../src/config/platforms.js';
|
||||
import { getPlatformPathPrefix } from '../../src/routing/platform-index.js';
|
||||
import { transformPath as transformPlatformPath } from '../../src/routing/platform-transformers.js';
|
||||
|
||||
describe('Platform module boundaries', () => {
|
||||
it('keeps the compatibility export wired to the platform catalog', () => {
|
||||
expect(PLATFORMS).toBe(PLATFORM_CATALOG);
|
||||
});
|
||||
|
||||
it('sorts platform keys by the longest routable prefix first', () => {
|
||||
const prefixLengths = SORTED_PLATFORMS.map(
|
||||
platformKey => getPlatformPathPrefix(platformKey).length
|
||||
);
|
||||
|
||||
prefixLengths.forEach((length, index) => {
|
||||
if (index < prefixLengths.length - 1) {
|
||||
expect(length).toBeGreaterThanOrEqual(prefixLengths[index + 1]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('routes legacy transform imports through the dedicated transformer module', () => {
|
||||
expect(transformPath('/crates/?q=tokio', 'crates')).toBe(
|
||||
transformPlatformPath('/crates/?q=tokio', 'crates')
|
||||
);
|
||||
expect(transformPath('/jenkins/test-path', 'jenkins')).toBe('/current/test-path');
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { PLATFORMS, transformPath } from '../../src/config/platforms.js';
|
||||
import { PLATFORM_CATALOG as PLATFORMS } from '../../src/config/platform-catalog.js';
|
||||
import { transformPath } from '../../src/routing/platform-transformers.js';
|
||||
|
||||
describe('Platform Configuration', () => {
|
||||
describe('Platform Definitions', () => {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
createPlatformEntries,
|
||||
extractPlatformsModule,
|
||||
loadPlatformsFromSource
|
||||
} from '../../skills/xget/scripts/xget.mjs';
|
||||
|
||||
describe('xget skill script', () => {
|
||||
it('extracts platform data from the new platform catalog source', () => {
|
||||
const source = `export const PLATFORM_CATALOG = {
|
||||
gh: 'https://github.com',
|
||||
'cr-ghcr': 'https://ghcr.io'
|
||||
};
|
||||
|
||||
export const PLATFORMS = PLATFORM_CATALOG;
|
||||
`;
|
||||
|
||||
expect(extractPlatformsModule(source)).toEqual({
|
||||
gh: 'https://github.com',
|
||||
'cr-ghcr': 'https://ghcr.io'
|
||||
});
|
||||
});
|
||||
|
||||
it('still accepts the legacy PLATFORMS object source', () => {
|
||||
const source = `export const PLATFORMS = {
|
||||
npm: 'https://registry.npmjs.org'
|
||||
};
|
||||
`;
|
||||
|
||||
expect(extractPlatformsModule(source)).toEqual({
|
||||
npm: 'https://registry.npmjs.org'
|
||||
});
|
||||
});
|
||||
|
||||
it('loads categorized platform entries from the extracted source', () => {
|
||||
const entries = loadPlatformsFromSource(`export const PLATFORM_CATALOG = {
|
||||
gh: 'https://github.com',
|
||||
'ip-openai': 'https://api.openai.com',
|
||||
'cr-ghcr': 'https://ghcr.io'
|
||||
};
|
||||
`);
|
||||
|
||||
expect(entries).toEqual(
|
||||
createPlatformEntries({
|
||||
gh: 'https://github.com',
|
||||
'ip-openai': 'https://api.openai.com',
|
||||
'cr-ghcr': 'https://ghcr.io'
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user