Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions packages/cloud/cloudflare/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,31 @@ describe('Cloudflare cloud adapter', () => {
expect(instances.find((instance) => instance.id === 'tunnel:tun-1')?.status).toBe('running');
});

it('continues list pagination when Cloudflare omits total_pages', async () => {
const fetchMock = vi.fn(async (url: string) => {
const { searchParams } = new URL(url);
const page = searchParams.get('page');
if (page === '1') {
return ok(Array.from({ length: 100 }, (_, index) => ({
queue_id: `queue-${index}`,
queue_name: `jobs-${index}`,
created_on: '2026-06-14T00:00:00Z',
})));
}
if (page === '2') {
return ok([{ queue_id: 'queue-100', queue_name: 'jobs-100', created_on: '2026-06-14T00:00:00Z' }]);
}
throw new Error(`unexpected url ${url}`);
});
vi.stubGlobal('fetch', fetchMock);

const instances = await adapter.list(connectCtx(), { accountId: 'acct-1', resourceType: 'queue' });

expect(instances).toHaveLength(101);
expect(instances.at(-1)?.id).toBe('queue:queue-100');
expect(fetchMock).toHaveBeenCalledTimes(2);
});

it('checks status using the prefixed resource id', async () => {
const fetchMock = vi.fn(async (url: string) => {
expect(url).toBe(`${API}/accounts/acct-1/queues/queue-1`);
Expand All @@ -137,6 +162,24 @@ describe('Cloudflare cloud adapter', () => {
expect(instance).toMatchObject({ id: 'queue:queue-1', status: 'running', sku: 'queue' });
});

it('maps Cloudflare tunnel active and errored statuses', async () => {
const fetchMock = vi.fn(async (url: string) => {
if (url.endsWith('/cfd_tunnel/tun-active')) {
return ok({ id: 'tun-active', name: 'edge-active', status: 'active', created_at: '2026-06-14T00:00:00Z' });
}
if (url.endsWith('/cfd_tunnel/tun-errored')) {
return ok({ id: 'tun-errored', name: 'edge-errored', status: 'errored', created_at: '2026-06-14T00:00:00Z' });
}
throw new Error(`unexpected url ${url}`);
});
vi.stubGlobal('fetch', fetchMock);

await expect(adapter.status(connectCtx(), 'tunnel:tun-active', { accountId: 'acct-1' }))
.resolves.toMatchObject({ id: 'tunnel:tun-active', status: 'running' });
await expect(adapter.status(connectCtx(), 'tunnel:tun-errored', { accountId: 'acct-1' }))
.resolves.toMatchObject({ id: 'tunnel:tun-errored', status: 'failed' });
});

it('requires a caller-supplied tunnel secret when creating a tunnel', async () => {
const fetchMock = vi.fn();
vi.stubGlobal('fetch', fetchMock);
Expand Down
26 changes: 18 additions & 8 deletions packages/cloud/cloudflare/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,17 +263,24 @@ async function cfListAll<T>(
path: string,
arrayKey: string,
): Promise<T[]> {
const perPage = 100;
const items: T[] = [];
let page = 1;
let totalPages = 1;
let shouldContinue = true;

do {
const separator = path.includes('?') ? '&' : '?';
const { result, resultInfo } = await cfRequest<unknown>(ctx, config, 'GET', `${path}${separator}page=${page}&per_page=100`);
items.push(...arrayFromResult<T>(result, arrayKey));
totalPages = typeof resultInfo?.total_pages === 'number' ? resultInfo.total_pages : 1;
const { result, resultInfo } = await cfRequest<unknown>(ctx, config, 'GET', `${path}${separator}page=${page}&per_page=${perPage}`);
const pageItems = arrayFromResult<T>(result, arrayKey);
items.push(...pageItems);

if (typeof resultInfo?.total_pages === 'number') {
shouldContinue = page < resultInfo.total_pages;
} else {
shouldContinue = pageItems.length >= perPage;
}
page += 1;
} while (page <= totalPages);
} while (shouldContinue);

return items;
}
Expand Down Expand Up @@ -477,9 +484,12 @@ function tunnelInstance(tunnel: Tunnel, kind: InstanceKind, quote: Quote, fallba
}

function tunnelStatus(status: string | undefined): Instance['status'] {
if (status === 'healthy') return 'running';
if (status === 'inactive' || status === 'down') return 'stopped';
if (status === 'degraded') return 'failed';
const normalized = status?.toLowerCase();
if (!normalized) return 'provisioning';
if (normalized === 'healthy' || normalized === 'active' || normalized === 'running') return 'running';
if (normalized === 'inactive' || normalized === 'down' || normalized === 'stopped') return 'stopped';
if (normalized === 'degraded' || normalized === 'errored' || normalized === 'error' || normalized === 'failed' || normalized === 'unhealthy') return 'failed';
if (normalized === 'pending' || normalized === 'provisioning' || normalized === 'initializing') return 'provisioning';
return 'provisioning';
}

Expand Down
Loading