Skip to content
Open
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
9 changes: 9 additions & 0 deletions .changeset/nitro-local-queue-origin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@workflow/nitro": patch
"@workflow/nuxt": patch
"@workflow/world-local": patch
---

Enables local queue origin config via Nitro runtime.


23 changes: 23 additions & 0 deletions docs/content/docs/deploying/world/local-world.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,29 @@ For advanced use cases like HTTPS or custom hostnames, you can override the enti
export WORKFLOW_LOCAL_BASE_URL=https://local.example.com:3000
```

**Nuxt / Nitro runtime config (optional):**

If you're using Nuxt / Nitro and are running your app behind a reverse proxy, tunnel, custom hostname, or non-standard port, configure the origin via runtime config. Add this to your `nuxt.config.ts` or `nitro.config.ts`:

```typescript title="nuxt.config.ts" lineNumbers
export default defineNuxtConfig({
modules: ["workflow/nuxt"],
runtimeConfig: {
// Server-only runtime config: this is internal queue traffic, not for the browser.
workflow: {
// Full origin (recommended)
baseUrl: "https://local.example.com:4443",
// Or build it from parts:
// protocol: "https",
// host: "local.example.com",
// port: 4443,
},
},
});
```

This works because Nuxt exposes runtime config via `useRuntimeConfig()` on the server, and the `workflow/nitro` integration uses it to set `WORKFLOW_LOCAL_BASE_URL` for local queue traffic. See Nuxt docs: `https://nuxt.com/docs/4.x/guide/going-further/runtime-config`.

**Programmatically:**

```typescript
Expand Down
11 changes: 11 additions & 0 deletions packages/nitro/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Nitro, NitroModule, RollupConfig } from 'nitro/types';
import { fileURLToPath } from 'node:url';
import { join } from 'pathe';
import { LocalBuilder, VercelBuilder } from './builders.js';
import { workflowTransformPlugin } from '@workflow/rollup';
Expand All @@ -12,6 +13,16 @@ export default {
const isVercelDeploy =
!nitro.options.dev && nitro.options.preset === 'vercel';

// Runtime plugin: allow configuring local queue origin via Nitro runtimeConfig
// (Nuxt feeds its runtimeConfig into Nitro, so this enables Nuxt runtimeConfig support too.)
nitro.options.plugins ||= [];
const runtimePluginPath = fileURLToPath(
new URL('./runtime/workflow-runtime-config', import.meta.url)
);
if (!nitro.options.plugins.includes(runtimePluginPath)) {
nitro.options.plugins.push(runtimePluginPath);
}

// Add transform plugin
nitro.hooks.hook('rollup:before', (_nitro: Nitro, config: RollupConfig) => {
(config.plugins as Array<unknown>).push(workflowTransformPlugin());
Expand Down
53 changes: 53 additions & 0 deletions packages/nitro/src/runtime/workflow-runtime-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { defineNitroPlugin } from 'nitro/~internal/runtime/plugin';
import { useRuntimeConfig } from 'nitro/runtime-config';

/**
* Bridge Nitro runtimeConfig -> Workflow local world env vars.
*
* This is framework-agnostic:
* - Works in Nuxt because Nuxt feeds runtimeConfig into Nitro.
* - Works in standalone Nitro if the user provides `runtimeConfig` in nitro.config.
*
* Supported keys:
* - `runtimeConfig.workflow.baseUrl` (preferred, private/server-only)
* - `runtimeConfig.public.workflow.baseUrl` (fallback)
* - `runtimeConfig.workflow.{protocol,host,port}` (preferred)
* - `runtimeConfig.public.workflow.{protocol,host,port}` (fallback)
*
* Result:
* - Sets `process.env.WORKFLOW_LOCAL_BASE_URL` if not already set.
*/
export default defineNitroPlugin(() => {
if (process.env.WORKFLOW_LOCAL_BASE_URL) return;

const runtimeConfig = useRuntimeConfig();
const workflow =
runtimeConfig?.workflow ?? runtimeConfig?.public?.workflow ?? undefined;

if (!workflow) return;

// Full baseUrl override (recommended)
if (typeof workflow.baseUrl === 'string' && workflow.baseUrl.length > 0) {
process.env.WORKFLOW_LOCAL_BASE_URL = workflow.baseUrl;
return;
}

// Construct from parts (protocol/host/port)
const protocol =
typeof workflow.protocol === 'string' && workflow.protocol.length > 0
? workflow.protocol
: undefined;
const host =
typeof workflow.host === 'string' && workflow.host.length > 0
? workflow.host
: undefined;
const port =
typeof workflow.port === 'number' || typeof workflow.port === 'string'
? String(workflow.port)
: undefined;

if (!protocol || !host) return;
process.env.WORKFLOW_LOCAL_BASE_URL = port
? `${protocol}://${host}:${port}`
: `${protocol}://${host}`;
});
8 changes: 6 additions & 2 deletions packages/world-local/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const getDataDirFromEnv = () => {
export const DEFAULT_RESOLVE_DATA_OPTION = 'all';

const getBaseUrlFromEnv = () => {
// Primary Workflow env var (documented)
return process.env.WORKFLOW_LOCAL_BASE_URL;
};

Expand All @@ -32,8 +33,11 @@ export const config = once<Config>(() => {
* 4. Auto-detected port via getPort (detect actual listening port)
*/
export async function resolveBaseUrl(config: Partial<Config>): Promise<string> {
if (config.baseUrl) {
return config.baseUrl;
// Allow resolving from env even if caller didn't pass merged config.
// (createLocalWorld() merges env config, but some callers use resolveBaseUrl directly.)
const envBaseUrl = getBaseUrlFromEnv();
if (config.baseUrl || envBaseUrl) {
return config.baseUrl ?? envBaseUrl!;
}

if (typeof config.port === 'number') {
Expand Down