diff --git a/packages/void/src/deploy.ts b/packages/void/src/deploy.ts index 40d7f0668..9c3f89b11 100644 --- a/packages/void/src/deploy.ts +++ b/packages/void/src/deploy.ts @@ -150,6 +150,7 @@ export async function prepareDeployArtifact( false, { bindings, + vars: config.worker?.vars, hasEmailHandlers: scan.email !== undefined && hasEmailHandlers(scan.email), migrations, schedules, diff --git a/packages/void/test/unit/deploy-artifact-preparation.test.ts b/packages/void/test/unit/deploy-artifact-preparation.test.ts index 44be5d856..0b1715402 100644 --- a/packages/void/test/unit/deploy-artifact-preparation.test.ts +++ b/packages/void/test/unit/deploy-artifact-preparation.test.ts @@ -42,6 +42,7 @@ it('packages real Worker and asset bytes without credentials or provider calls', expect(Buffer.from(result.assets['/index.html']!, 'base64').toString()).toBe('

Hello

'); expect(result.artifact.manifest.assetManifestVersion).toBe(2); expect(result.artifact.manifest.bindings).toEqual({}); + expect(result.artifact.manifest).not.toHaveProperty('vars'); expect(result.artifact.assetManifest['/index.html']).toMatchObject({ blake3: expect.stringMatching(/^[a-f0-9]{32}$/), md5: expect.stringMatching(/^[a-f0-9]{32}$/), @@ -62,6 +63,38 @@ it('packages real Worker and asset bytes without credentials or provider calls', }), ).not.toBe(digest); }); +it('includes configured Worker vars without copying dotenv or generated vars', async () => { + writeFileSync( + join(root, 'void.config.ts'), + "export default { worker: { vars: { API_URL: 'https://api.example.test', EMPTY_SETTING: '' } } };", + ); + writeFileSync(join(root, '.env'), 'API_URL=https://local.example.test\nLOCAL_ONLY=local-value\n'); + writeFileSync( + join(root, 'dist/server/wrangler.json'), + JSON.stringify({ + main: 'index.js', + compatibility_date: '2026-10-08', + compatibility_flags: ['nodejs_compat'], + vars: { API_URL: 'https://built.example.test', BUILD_ONLY: 'generated-value' }, + }), + ); + + const result = await prepareDeployArtifact(root); + + expect(result.artifact.manifest.vars).toEqual({ + API_URL: 'https://api.example.test', + EMPTY_SETTING: '', + }); + expect(fetch).not.toHaveBeenCalled(); +}); +it('omits empty configured Worker vars', async () => { + writeFileSync(join(root, 'void.config.ts'), 'export default { worker: { vars: {} } };'); + + const result = await prepareDeployArtifact(root); + + expect(result.artifact.manifest).not.toHaveProperty('vars'); + expect(fetch).not.toHaveBeenCalled(); +}); it('fails when Worker build output is missing', async () => { rmSync(join(root, 'dist/server'), { recursive: true }); await expect(prepareDeployArtifact(root)).rejects.toThrow('Worker output not found'); @@ -103,7 +136,7 @@ it('packages a detected Vite SPA as assets without a Worker or provider calls', it('honors static output directories and routing rules without packaging stale Worker output', async () => { writeFileSync( join(root, 'void.config.ts'), - `export default { inference: { appType: 'static', outputDir: 'site' }, routing: { notFound: '404-page' } };`, + `export default { inference: { appType: 'static', outputDir: 'site' }, routing: { notFound: '404-page' }, worker: { vars: { API_URL: 'https://api.example.test' } } };`, ); mkdirSync(join(root, 'site')); writeFileSync(join(root, 'site/index.html'), '

Static

'); @@ -117,6 +150,7 @@ it('honors static output directories and routing rules without packaging stale W redirectRules: [{ source: '/old', destination: '/new', status: 301 }], }); expect(result.artifact.workerFiles).toBeUndefined(); + expect(result.artifact.manifest).not.toHaveProperty('vars'); expect(Object.keys(result.assets)).toEqual(['/index.html']); expect(fetch).not.toHaveBeenCalled(); });