(options: CloudBootOptions)
| 53 | } |
| 54 | |
| 55 | export const bootCloud = async (options: CloudBootOptions): Promise<CloudBooted> => { |
| 56 | // Fresh dev DB per boot — the WorkOS emulator mints org ids from a |
| 57 | // per-process counter, so a persisted DB from a previous invocation |
| 58 | // collides with the new boot's ids (identities land in polluted orgs / |
| 59 | // org creation 500s). |
| 60 | const dbPath = resolve(cloudDir, ".e2e-stub-db"); |
| 61 | if (options.fresh ?? true) rmSync(dbPath, { recursive: true, force: true }); |
| 62 | |
| 63 | // Fresh worker state per boot, for the same reason as the DB: miniflare |
| 64 | // persists the Workers Cache API under .wrangler/state across dev-server |
| 65 | // runs, and the JWKS L2 cache (auth/jwks-cache.ts) lives there keyed by |
| 66 | // URL. The WorkOS emulator binds this checkout's stable port block, so run |
| 67 | // N+1's dev server serves run N's cached JWKS (stale-while-revalidate) |
| 68 | // against a fresh emulator's keys — every session verify then fails |
| 69 | // signature, falls into single-use refresh, and concurrent requests race |
| 70 | // each other's rotation into 401s. One run poisons the next. |
| 71 | if (options.fresh ?? true) { |
| 72 | rmSync(resolve(cloudDir, ".wrangler", "state"), { recursive: true, force: true }); |
| 73 | } |
| 74 | |
| 75 | // MCP access tokens minted by the emulator's OAuth server must carry the |
| 76 | // app's client id as audience (what the resource server verifies). |
| 77 | process.env.EMULATE_WORKOS_AUDIENCE = options.workosClientId; |
| 78 | const workos = await createEmulator({ |
| 79 | service: "workos", |
| 80 | port: options.workosPort, |
| 81 | ...(options.workosPublicUrl ? { baseUrl: options.workosPublicUrl } : {}), |
| 82 | }); |
| 83 | const autumn = await createEmulator({ |
| 84 | service: "autumn", |
| 85 | port: options.autumnPort, |
| 86 | // Seed the plan catalog so the billing UI (plans, eligibility, trial |
| 87 | // checkout) has real plans to render. Derived from autumn.config.ts. |
| 88 | seed: { autumn: { plans: AUTUMN_PLAN_SEED } }, |
| 89 | }); |
| 90 | |
| 91 | const workosUrl = options.workosPublicUrl ?? workos.url; |
| 92 | const env = { |
| 93 | // Real client, emulated service. The app derives the browser-facing |
| 94 | // authorize URL from WORKOS_API_URL, so it must be the PUBLIC origin. |
| 95 | WORKOS_API_URL: workosUrl, |
| 96 | AUTUMN_API_URL: autumn.url, |
| 97 | WORKOS_API_KEY: "sk_test_emulate", |
| 98 | WORKOS_CLIENT_ID: options.workosClientId, |
| 99 | WORKOS_COOKIE_PASSWORD: options.cookiePassword, |
| 100 | AUTUMN_SECRET_KEY: "am_test_emulate", |
| 101 | ENCRYPTION_KEY: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", |
| 102 | DATABASE_URL: `postgresql://postgres:postgres@127.0.0.1:${options.dbPort}/postgres`, |
| 103 | EXECUTOR_DIRECT_DATABASE_URL: "true", |
| 104 | CLOUDFLARE_INCLUDE_PROCESS_ENV: "true", |
| 105 | VITE_PUBLIC_SITE_URL: options.publicUrl, |
| 106 | // The AuthKit domain (MCP OAuth metadata + JWKS) is the emulator too. |
| 107 | MCP_AUTHKIT_DOMAIN: workosUrl, |
| 108 | MCP_RESOURCE_ORIGIN: options.publicUrl, |
| 109 | MCP_SESSION_TIMEOUT_MS: process.env.MCP_SESSION_TIMEOUT_MS, |
| 110 | MCP_PAUSED_SESSION_IDLE_TIMEOUT_MS: process.env.MCP_PAUSED_SESSION_IDLE_TIMEOUT_MS, |
| 111 | // See resident-runtime-cap.ts for why this value, and why it is safe to |
| 112 | // set unconditionally for the whole boot (same treatment as the execution |
no test coverage detected