From 58d3022e312011cef5c6e40d42ff36c56cede0e3 Mon Sep 17 00:00:00 2001 From: Aayush <21987529+aayushbtw@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:50:47 +0400 Subject: [PATCH 1/3] fix(deploy): read Cloudflare versions that have no bindings --- packages/void/src/cli/cf-version-runner.ts | 13 +++++----- .../void/test/unit/cf-version-runner.test.ts | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/void/src/cli/cf-version-runner.ts b/packages/void/src/cli/cf-version-runner.ts index f6a887990..b8c999900 100644 --- a/packages/void/src/cli/cf-version-runner.ts +++ b/packages/void/src/cli/cf-version-runner.ts @@ -584,16 +584,17 @@ export function createCfVersionRunner(options: { return result; } const version = result.data; - if ( - !record(version) || - version.id !== operation.versionId || - !Array.isArray(version.bindings) - ) { + if (!record(version) || version.id !== operation.versionId) { + return failed('The Cloudflare Worker version was not readable.'); + } + // cf omits `bindings` for a version that has none. + const bindings = version.bindings ?? []; + if (!Array.isArray(bindings)) { return failed('The Cloudflare Worker version was not readable.'); } return { ...result, - data: { ...version, resources: { bindings: version.bindings } }, + data: { ...version, bindings, resources: { bindings } }, }; } diff --git a/packages/void/test/unit/cf-version-runner.test.ts b/packages/void/test/unit/cf-version-runner.test.ts index 0e534b39e..d2b27ead7 100644 --- a/packages/void/test/unit/cf-version-runner.test.ts +++ b/packages/void/test/unit/cf-version-runner.test.ts @@ -147,6 +147,32 @@ it( REAL_CF_TIMEOUT_MS, ); +it('reads a version that cf returns without bindings as one with no bindings', async () => { + const prepared = await output(); + try { + const runner = createCfVersionRunner({ + output: prepared, + workerName: 'void-test', + runCf: () => ({ + status: 0, + stdout: JSON.stringify({ id: 'version-id', number: 35 }), + stderr: '', + }), + verifyResources: async () => { + throw new Error('A read must not provision or probe resources'); + }, + }); + const result = await runner( + { type: 'versions.view', versionId: 'version-id', workerName: 'void-test' }, + { capture: true }, + ); + expect(result.status).toBe(0); + expect(result.data).toMatchObject({ bindings: [], resources: { bindings: [] } }); + } finally { + await prepared.dispose(); + } +}); + it( 'preserves real cf provider codes so a missing Worker can reach its first upload', async () => { From db436cb92a39c94548db990c636af59e2ea917b7 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Sun, 18 Oct 2026 15:06:00 +0800 Subject: [PATCH 2/3] fix(deploy): name Cloudflare as the source of the missing bindings `cf workers versions get` prints the API response unchanged, so the field is missing from Cloudflare's own response. Co-Authored-By: Claude Opus 5.5 (1M context) --- packages/void/src/cli/cf-version-runner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/void/src/cli/cf-version-runner.ts b/packages/void/src/cli/cf-version-runner.ts index b8c999900..a6d8b1aba 100644 --- a/packages/void/src/cli/cf-version-runner.ts +++ b/packages/void/src/cli/cf-version-runner.ts @@ -587,7 +587,7 @@ export function createCfVersionRunner(options: { if (!record(version) || version.id !== operation.versionId) { return failed('The Cloudflare Worker version was not readable.'); } - // cf omits `bindings` for a version that has none. + // Cloudflare omits `bindings` for a version that has none. const bindings = version.bindings ?? []; if (!Array.isArray(bindings)) { return failed('The Cloudflare Worker version was not readable.'); From d1f437e380abf0a7e0bbefe9984fa2c73f182232 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Sun, 18 Oct 2026 15:13:07 +0800 Subject: [PATCH 3/3] fix(deploy): still reject a version whose bindings are null Only a missing `bindings` field means "no bindings". A `null` value is malformed and must not pass as proof that the version has no secrets. Co-Authored-By: Claude Opus 5.5 (1M context) --- packages/void/src/cli/cf-version-runner.ts | 2 +- .../void/test/unit/cf-version-runner.test.ts | 24 ++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/void/src/cli/cf-version-runner.ts b/packages/void/src/cli/cf-version-runner.ts index a6d8b1aba..c8b8c41be 100644 --- a/packages/void/src/cli/cf-version-runner.ts +++ b/packages/void/src/cli/cf-version-runner.ts @@ -588,7 +588,7 @@ export function createCfVersionRunner(options: { return failed('The Cloudflare Worker version was not readable.'); } // Cloudflare omits `bindings` for a version that has none. - const bindings = version.bindings ?? []; + const bindings = version.bindings === undefined ? [] : version.bindings; if (!Array.isArray(bindings)) { return failed('The Cloudflare Worker version was not readable.'); } diff --git a/packages/void/test/unit/cf-version-runner.test.ts b/packages/void/test/unit/cf-version-runner.test.ts index d2b27ead7..e1fb6bfe2 100644 --- a/packages/void/test/unit/cf-version-runner.test.ts +++ b/packages/void/test/unit/cf-version-runner.test.ts @@ -147,17 +147,17 @@ it( REAL_CF_TIMEOUT_MS, ); -it('reads a version that cf returns without bindings as one with no bindings', async () => { +it.each([ + { label: 'missing', version: { id: 'version-id', number: 35 }, readable: true }, + { label: 'null', version: { id: 'version-id', number: 35, bindings: null }, readable: false }, + { label: 'an object', version: { id: 'version-id', number: 35, bindings: {} }, readable: false }, +])('reads a version whose bindings are $label', async ({ version, readable }) => { const prepared = await output(); try { const runner = createCfVersionRunner({ output: prepared, workerName: 'void-test', - runCf: () => ({ - status: 0, - stdout: JSON.stringify({ id: 'version-id', number: 35 }), - stderr: '', - }), + runCf: () => ({ status: 0, stdout: JSON.stringify(version), stderr: '' }), verifyResources: async () => { throw new Error('A read must not provision or probe resources'); }, @@ -166,8 +166,16 @@ it('reads a version that cf returns without bindings as one with no bindings', a { type: 'versions.view', versionId: 'version-id', workerName: 'void-test' }, { capture: true }, ); - expect(result.status).toBe(0); - expect(result.data).toMatchObject({ bindings: [], resources: { bindings: [] } }); + if (readable) { + // Cloudflare omits `bindings` for a version that has none. + expect(result.status).toBe(0); + expect(result.data).toMatchObject({ bindings: [], resources: { bindings: [] } }); + } else { + expect(result).toMatchObject({ + status: 1, + stderr: 'The Cloudflare Worker version was not readable.', + }); + } } finally { await prepared.dispose(); }