void 0.10.8 → 0.10.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/{auth-cmd-DlgNwByu.mjs → auth-cmd-Cw1edJdZ.mjs} +2 -2
  2. package/dist/{build-cmd-Br8qL0rA.mjs → build-cmd-C6kNDJBv.mjs} +1 -1
  3. package/dist/{cache-wH-mP8UE.mjs → cache-DIUSnIjJ.mjs} +1 -1
  4. package/dist/{cancel-deploy-BEBOEgtu.mjs → cancel-deploy-BosnzEHC.mjs} +1 -1
  5. package/dist/cli/cli.mjs +77 -28
  6. package/dist/{client-Cj96iiBH.mjs → client-C9FG6Vzc.mjs} +8 -0
  7. package/dist/{config-1twldYCW.mjs → config-BcAeIPe3.mjs} +6 -0
  8. package/dist/{create-project-DD9n8Ho-.mjs → create-project-CjYX23M_.mjs} +1 -1
  9. package/dist/{db-DjKE2-A-.mjs → db-BvKV34IK.mjs} +4 -4
  10. package/dist/{delete-D2kr3Kmk.mjs → delete-CThKLXfi.mjs} +1 -1
  11. package/dist/{deploy-C4PbkFyE.mjs → deploy-B02JyKP9.mjs} +59 -16
  12. package/dist/{domain-_luIsM_B.mjs → domain-DMrBabBQ.mjs} +1 -1
  13. package/dist/{env-CO5XAS9t.mjs → env-BfG7O71F.mjs} +1 -1
  14. package/dist/env-raw-Cx8ElDdj.mjs +58 -0
  15. package/dist/{gen-Cf79J4aw.mjs → gen-Bee261SV.mjs} +1 -1
  16. package/dist/{github-cmd-B6OX9c7d.mjs → github-cmd-DfeFEasr.mjs} +101 -2
  17. package/dist/{headers-BwvFGhkx.mjs → headers-CTAjX-UO.mjs} +1 -1
  18. package/dist/index.d.mts +13 -1
  19. package/dist/index.mjs +70 -31
  20. package/dist/{init-KirOzVDs.mjs → init-xfwW7cLp.mjs} +4 -4
  21. package/dist/{link-CUmiosyb.mjs → link-Co136MEs.mjs} +2 -2
  22. package/dist/{list-3GEw7b6m.mjs → list-BKDjA3M5.mjs} +1 -1
  23. package/dist/{login-DJReaT_Q.mjs → login-CrOTnR_6.mjs} +1 -1
  24. package/dist/{logs-D-rQ56Lq.mjs → logs-DlAg25ww.mjs} +1 -1
  25. package/dist/{mcp-D7yc0dXY.mjs → mcp-BNjMGMB0.mjs} +1 -1
  26. package/dist/{node-yFFk626c.mjs → node-CnE-SZCk.mjs} +1 -1
  27. package/dist/pages/index.mjs +2 -2
  28. package/dist/{prepare-C_cVurhP.mjs → prepare-BRyC4TCC.mjs} +1 -1
  29. package/dist/{project-cmd-DnU7u9QF.mjs → project-cmd-B2bcKefD.mjs} +8 -8
  30. package/dist/{rollback-Yh7bCKob.mjs → rollback-9dfmB-YZ.mjs} +1 -1
  31. package/dist/runtime/ai.mjs +113 -15
  32. package/dist/runtime/env-public.mjs +9 -4
  33. package/dist/runtime/env.d.mts +14 -1
  34. package/dist/runtime/env.mjs +24 -10
  35. package/dist/runtime/isr-cache.d.mts +207 -0
  36. package/dist/runtime/isr-cache.mjs +523 -0
  37. package/dist/runtime/isr.d.mts +4 -3
  38. package/dist/runtime/isr.mjs +44 -5
  39. package/dist/runtime/live.mjs +1 -1
  40. package/dist/runtime/sandbox.mjs +1 -1
  41. package/dist/{secret-u7FRvg8d.mjs → secret-DeMnMcV0.mjs} +1 -1
  42. package/package.json +3 -2
  43. package/schema.json +11 -0
  44. package/skills/void/docs/integrations/cloudflare.md +107 -0
  45. package/skills/void/docs/node_modules/void/{AGENTS.md → CLAUDE.md} +14 -0
  46. package/skills/void/docs/reference/cli.md +23 -0
  47. package/dist/env-raw-CoS20LHP.mjs +0 -32
package/dist/index.d.mts CHANGED
@@ -7,6 +7,18 @@ import { Plugin } from "vite";
7
7
 
8
8
  //#region src/index.d.ts
9
9
  /**
10
+ * Decide whether to inject a Workers AI binding into the built wrangler config.
11
+ *
12
+ * Build-only: dev/miniflare has no local Workers AI, so serve must never bind
13
+ * `env.AI` (dev uses the P2 `__VOID_TOKEN` proxy path instead).
14
+ *
15
+ * `!resolvedAi` is load-bearing: mergeBindings' return is defu-merged over the
16
+ * resolved root-wrangler config and wins, so skipping when the user already
17
+ * declared an `ai` binding avoids clobbering theirs. `!resultAi` is harmless
18
+ * defense-in-depth (nothing else in mergeBindings sets `result.ai`).
19
+ */
20
+ declare function shouldBindWorkersAi(command: string, needsAI: boolean, resolvedAi: unknown, resultAi: unknown): boolean;
21
+ /**
10
22
  * Void plugin — the main entry point.
11
23
  *
12
24
  * Composes:
@@ -29,4 +41,4 @@ declare function voidPlugin(options?: {
29
41
  auxiliaryWorkers?: PluginConfig["auxiliaryWorkers"];
30
42
  }): Array<Plugin>;
31
43
  //#endregion
32
- export { type AuthUser, type BasicAuthOptions, type CloudContext, type CloudContextVariables, type CloudEnv, type Deferred, type DeferredState, type HandlerInput, type HeadDescriptor, type InferProps, type QueueBatch, type QueueMap, type QueueMessage, type QueueRetryOptions, type RenderAssetTags, type TypedHandler, type TypedQueueHandler, type ValidatorSlots, VoidAssetRewriteError, basicAuth, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled, voidPlugin };
44
+ export { type AuthUser, type BasicAuthOptions, type CloudContext, type CloudContextVariables, type CloudEnv, type Deferred, type DeferredState, type HandlerInput, type HeadDescriptor, type InferProps, type QueueBatch, type QueueMap, type QueueMessage, type QueueRetryOptions, type RenderAssetTags, type TypedHandler, type TypedQueueHandler, type ValidatorSlots, VoidAssetRewriteError, basicAuth, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled, shouldBindWorkersAi, voidPlugin };
package/dist/index.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  import { r as __toESM } from "./rolldown-runtime-DJK8HYOj.mjs";
2
2
  import { a as join, n as dirname, s as relative } from "./pathe.M-eThtNZ-CQzLbt4c.mjs";
3
3
  import { M as require_picocolors } from "./agents-Bmr5tFFb.mjs";
4
- import { C as hasApiRoutes, S as resolveSandboxConfig, T as resolveAssetRoutingPolicy, _ as readWranglerCompat, a as prerenderPagesNode, b as SANDBOX_MIGRATION_TAG, c as stripInternalEnvKeys, d as ensureWranglerCompatibilityDate, f as ensureWranglerNodejsAlsFlag, g as readExistingLiveBindingConfig, h as normalizeWranglerCompatibilityFlags, i as prerenderPages, l as LIVE_MIGRATION_TAG, m as nextDurableObjectMigrationTag, n as resolveDistDir, o as filterLoadedEnv, p as hasDurableObjectSqliteMigration, r as resolveWorkerDirName, s as resolveRemoteMode, u as ensureWranglerCompat, v as resolveLiveBindingConfig, w as hasDocumentRoutes, x as isSandboxEnabled, y as syncWranglerBindings } from "./deploy-C4PbkFyE.mjs";
4
+ import { C as isSandboxEnabled, D as resolveAssetRoutingPolicy, E as hasDocumentRoutes, S as SANDBOX_MIGRATION_TAG, T as hasApiRoutes, _ as normalizeWranglerCompatibilityFlags, a as prerenderPagesNode, b as resolveLiveBindingConfig, c as filterLoadedEnv, d as LIVE_MIGRATION_TAG, f as ensureWranglerCompat, g as nextDurableObjectMigrationTag, h as hasDurableObjectSqliteMigration, i as prerenderPages, l as resolveRemoteMode, m as ensureWranglerNodejsAlsFlag, n as resolveDistDir, o as collectRevalidate, p as ensureWranglerCompatibilityDate, r as resolveWorkerDirName, s as hasRevalidate, u as stripInternalEnvKeys, v as readExistingLiveBindingConfig, w as resolveSandboxConfig, x as syncWranglerBindings, y as readWranglerCompat } from "./deploy-B02JyKP9.mjs";
5
5
  import { i as resolveProjectPaths, n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-tpdR1mJR.mjs";
6
6
  import { a as scanQueuesSync, c as loadVoidAuthConfig, i as scanWebSocketRoutesSync, n as scanRoutePatternsSync, o as scanJobsSync, r as scanRoutes, s as findVoidAuthConfig, t as scanMiddlewareSync } from "./scan-i7Yz54fv.mjs";
7
- import { b as readProjectConfig, f as getToken, m as isStagingMode } from "./client-Cj96iiBH.mjs";
7
+ import { b as readProjectConfig, f as getToken, m as isStagingMode } from "./client-C9FG6Vzc.mjs";
8
8
  import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-D9uSVVpA.mjs";
9
- import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-1twldYCW.mjs";
9
+ import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-BcAeIPe3.mjs";
10
10
  import { a as SSR_CLIENT_ENTRY_RELATIVE_PATHS, c as validateSsrEntries, f as isIdentifier, g as walk, i as inferProjectBindings, l as validateSsrEntry, m as isMemberExpression, n as FRAMEWORK_SCAN_DIRS, o as SSR_SERVER_ENTRY_RELATIVE_PATHS, p as isLiteral, r as detectFramework, t as DEFAULT_SCAN_DIRS, u as inferBindingsFromSource } from "./plugin-inference-CJxi_fWI.mjs";
11
11
  import { i as voidWarn, n as voidError, r as voidLog, t as setShowTimestamp } from "./log-ChfPKsVd.mjs";
12
12
  import { r as parseDotenvFile } from "./dotenv-D_UbC_vc.mjs";
@@ -17,8 +17,8 @@ import { n as findEnvFileForPaths, r as generateEnvTypes, t as findEnvFile } fro
17
17
  import { a as loadUserEnvFile } from "./env-validation-CeC2FL66.mjs";
18
18
  import { i as resolveLayoutIds } from "./scan-Dp_Gyzs3.mjs";
19
19
  import { n as STAGING_PROXY_URL, t as DEFAULT_PROXY_URL } from "./proxy-D-3_D-Gl.mjs";
20
- import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-BwvFGhkx.mjs";
21
- import { t as INTERNAL_BINDING_PREFIXES } from "./env-raw-CoS20LHP.mjs";
20
+ import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-CTAjX-UO.mjs";
21
+ import { n as INTERNAL_BINDING_PREFIXES, t as INTERNAL_BINDING_EXACT_NAMES } from "./env-raw-Cx8ElDdj.mjs";
22
22
  import { n as defer } from "./defer-YsYDUoii.mjs";
23
23
  import { VoidAssetRewriteError, basicAuth, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled } from "./runtime/handler.mjs";
24
24
  import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, writeFileSync, writeSync } from "node:fs";
@@ -1686,7 +1686,7 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
1686
1686
  if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
1687
1687
  }
1688
1688
  if (auth?.enabled) {
1689
- const { runLocalVoidAuthMigrations } = await import("./node-yFFk626c.mjs");
1689
+ const { runLocalVoidAuthMigrations } = await import("./node-CnE-SZCk.mjs");
1690
1690
  await runLocalVoidAuthMigrations(root, dialect, persistRoot, databaseId, auth.configPath);
1691
1691
  voidLog("Applied Better Auth migrations");
1692
1692
  }
@@ -2534,7 +2534,7 @@ function pushDefaultErrorPageHelpers(lines, options) {
2534
2534
  * - Middleware registered via app.use()
2535
2535
  * - Routes registered with lazy-loaded handlers and auto-conversion
2536
2536
  */
2537
- function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, options, pages = null) {
2537
+ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, options, pages = null, mergedRevalidate) {
2538
2538
  const lines = [];
2539
2539
  const auth = options.auth;
2540
2540
  const webSockets = scan.websockets ?? [];
@@ -2621,7 +2621,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
2621
2621
  lines.push("function __voidAnnotateRouting(response, trace) {");
2622
2622
  lines.push(" if (!__VOID_DEV_ROUTING || !response) return response;");
2623
2623
  lines.push(" const headers = new Headers(response.headers);");
2624
- lines.push(" try { headers.set(\"X-Void-Routing\", trace); } catch { return response; }");
2624
+ lines.push(" headers.set(\"X-Void-Routing\", trace);");
2625
2625
  lines.push(" return new Response(response.body, { status: response.status, statusText: response.statusText, headers });");
2626
2626
  lines.push("}");
2627
2627
  lines.push("");
@@ -3134,6 +3134,8 @@ window.__vite_plugin_react_preamble_installed__ = true;
3134
3134
  lines.push("function __getRawEnv() { return __rawEnvStore.getStore(); }");
3135
3135
  lines.push(`export const __VOID_BUILD_KEY = ${JSON.stringify(crypto.randomUUID())};`);
3136
3136
  lines.push(`export const __VOID_DEV_TRIGGER_TOKEN = ${JSON.stringify(options.devTriggerToken ?? crypto.randomUUID())};`);
3137
+ lines.push(`export const __VOID_ISR_REVALIDATE = ${JSON.stringify(mergedRevalidate)};`);
3138
+ lines.push(`export const __VOID_ISR_DEPLOYMENT_ID = ${JSON.stringify(crypto.randomUUID())};`);
3137
3139
  lines.push("");
3138
3140
  lines.push(`export default app;`);
3139
3141
  return lines.join("\n");
@@ -3613,7 +3615,7 @@ ${spaFallbackCode}
3613
3615
  * remote mode (`__VOID_REMOTE`) and replace local bindings with
3614
3616
  * proxy-backed versions.
3615
3617
  */
3616
- function compileWorkerEntry(hasQueues = false, remote, assetsDir = "assets", webSocketClasses = [], runtimeEnvPath = "void/_env", envSchemaModule, sandbox, serveSpaFallback = false, live) {
3618
+ function compileWorkerEntry(hasQueues = false, remote, assetsDir = "assets", webSocketClasses = [], runtimeEnvPath = "void/_env", envSchemaModule, sandbox, serveSpaFallback = false, live, isr) {
3617
3619
  const remotePath = remote?.remotePath;
3618
3620
  const remoteBindingNames = JSON.stringify(remote?.bindingNames);
3619
3621
  const createRemoteEnvCall = `createRemoteEnv(env${remote?.bindingNames ? `, ${remoteBindingNames}` : ""})`;
@@ -3633,7 +3635,8 @@ function compileWorkerEntry(hasQueues = false, remote, assetsDir = "assets", web
3633
3635
  const remoteImport = remote ? `import { createRemoteEnv } from ${JSON.stringify(remote.remotePath)};\n` : "";
3634
3636
  const envSchemaImport = envSchemaModule ? `import ${JSON.stringify(envSchemaModule.importPath)};\nimport { _validateEnvOnce } from ${JSON.stringify(envSchemaModule.helperPath)};\n` : "";
3635
3637
  const envValidateCall = envSchemaModule ? " await _validateEnvOnce(env);\n" : "";
3636
- const prefixCheck = INTERNAL_BINDING_PREFIXES.map((p) => `prop.startsWith(${JSON.stringify(p)})`).join(" || ");
3638
+ const exactNames = isr ? INTERNAL_BINDING_EXACT_NAMES : [];
3639
+ const prefixCheck = [...INTERNAL_BINDING_PREFIXES.map((p) => `prop.startsWith(${JSON.stringify(p)})`), ...exactNames.map((n) => `prop === ${JSON.stringify(n)}`)].join(" || ");
3637
3640
  const filterBindingsHelper = `
3638
3641
  function __filterInternalBindings(env) {
3639
3642
  return new Proxy(env, {
@@ -3647,7 +3650,7 @@ function __filterInternalBindings(env) {
3647
3650
  },
3648
3651
  ownKeys(target) {
3649
3652
  return Reflect.ownKeys(target).filter(
3650
- (key) => typeof key !== "string" || (${INTERNAL_BINDING_PREFIXES.map((p) => `!key.startsWith(${JSON.stringify(p)})`).join(" && ")})
3653
+ (key) => typeof key !== "string" || (${[...INTERNAL_BINDING_PREFIXES.map((p) => `!key.startsWith(${JSON.stringify(p)})`), ...exactNames.map((n) => `key !== ${JSON.stringify(n)}`)].join(" && ")})
3651
3654
  );
3652
3655
  },
3653
3656
  getOwnPropertyDescriptor(target, prop) {
@@ -3658,27 +3661,36 @@ function __filterInternalBindings(env) {
3658
3661
  }
3659
3662
  `;
3660
3663
  const assetHelper = compileAssetFallbackHelper(assetsDir, serveSpaFallback);
3664
+ const isrImport = isr ? `import { handleIsr } from ${JSON.stringify(isr.runtimePath)};\nimport { __VOID_ISR_REVALIDATE, __VOID_ISR_DEPLOYMENT_ID } from 'virtual:void-routes';\n` : "";
3665
+ const isrEnvCapture = isr ? ` env.__VOID_ISR_DEPLOYMENT_ID ??= __VOID_ISR_DEPLOYMENT_ID;\n env.__VOID_ISR_HOST = new URL(request.url).hostname;\n` : "";
3666
+ const isrDeploymentStamp = isr ? ` env.__VOID_ISR_DEPLOYMENT_ID ??= __VOID_ISR_DEPLOYMENT_ID;\n` : "";
3667
+ const renderBlock = isr ? ` const __render = async (req) => serveWithAssets(req, env, await app.fetch(req, userEnv, ctx));
3668
+ if (env.ISR_CACHE) {
3669
+ const __isr = await handleIsr(request, env, ctx, { revalidate: __VOID_ISR_REVALIDATE, deploymentId: __VOID_ISR_DEPLOYMENT_ID }, __render);
3670
+ if (__isr) return __isr;
3671
+ }
3672
+ return __render(request);` : ` return serveWithAssets(request, env, await app.fetch(request, userEnv, ctx));`;
3661
3673
  const fetchHandler = remotePath ? `async (request, env, ctx) => {
3662
3674
  ${envValidateCall} const ws = await __voidHandleWebSocket(request, env);
3663
3675
  if (ws) return ws;
3664
3676
  if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};
3665
- return __withRawEnv(env, () => {
3677
+ ${isrEnvCapture} return __withRawEnv(env, () => {
3666
3678
  const userEnv = __filterInternalBindings(env);
3667
3679
  return withRuntimeEnv(env, async () => {
3668
3680
  const internal = await __handleInternal(request, env, ctx);
3669
3681
  if (internal) return internal;
3670
- return serveWithAssets(request, env, await app.fetch(request, userEnv, ctx));
3682
+ ${renderBlock}
3671
3683
  });
3672
3684
  });
3673
3685
  }` : `async (request, env, ctx) => {
3674
3686
  ${envValidateCall} const ws = await __voidHandleWebSocket(request, env);
3675
3687
  if (ws) return ws;
3676
- return __withRawEnv(env, () => {
3688
+ ${isrEnvCapture} return __withRawEnv(env, () => {
3677
3689
  const userEnv = __filterInternalBindings(env);
3678
3690
  return withRuntimeEnv(env, async () => {
3679
3691
  const internal = await __handleInternal(request, env, ctx);
3680
3692
  if (internal) return internal;
3681
- return serveWithAssets(request, env, await app.fetch(request, userEnv, ctx));
3693
+ ${renderBlock}
3682
3694
  });
3683
3695
  });
3684
3696
  }`;
@@ -3704,18 +3716,18 @@ ${envValidateCall} const ws = await __voidHandleWebSocket(request, env);
3704
3716
  }`;
3705
3717
  const scheduledHandler = remotePath ? `async function handleScheduled(controller, env, ctx) {
3706
3718
  ${envValidateCall} if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};
3707
- const userEnv = __filterInternalBindings(env);
3719
+ ${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
3708
3720
  return withRuntimeEnv(env, () => scheduled(controller, userEnv, ctx));
3709
3721
  }` : `async function handleScheduled(controller, env, ctx) {
3710
- ${envValidateCall} const userEnv = __filterInternalBindings(env);
3722
+ ${envValidateCall}${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
3711
3723
  return withRuntimeEnv(env, () => scheduled(controller, userEnv, ctx));
3712
3724
  }`;
3713
3725
  if (hasQueues) return `${sandboxImport}${liveRuntimeImport}import app, { scheduled, __voidCollectPrerenderPaths, __voidHandleInternalRoute, __voidHandleWebSocket, __withRawEnv, __processQueue${webSocketClasses.length > 0 ? `, ${webSocketClasses.join(", ")}` : ""}${liveRouteImport} } from 'virtual:void-routes';
3714
- ${runtimeEnvImport}${envSchemaImport}${remoteImport}${filterBindingsHelper}${assetHelper}
3726
+ ${runtimeEnvImport}${envSchemaImport}${remoteImport}${isrImport}${filterBindingsHelper}${assetHelper}
3715
3727
  ${internalRouteHelper}
3716
3728
  ${scheduledHandler}
3717
3729
  async function queue(batch, env, ctx) {
3718
- ${envValidateCall}${remotePath ? ` if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};\n` : ""} const userEnv = __filterInternalBindings(env);
3730
+ ${envValidateCall}${remotePath ? ` if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};\n` : ""}${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
3719
3731
  const messages = [...batch.messages].map(m => ({
3720
3732
  id: m.id, timestamp: m.timestamp.toISOString(),
3721
3733
  body: m.body, attempts: m.attempts,
@@ -3740,7 +3752,7 @@ ${liveExport}
3740
3752
  ${sandboxExport}
3741
3753
  export default { fetch: ${fetchHandler}, scheduled: handleScheduled, queue };`;
3742
3754
  return `${sandboxImport}${liveRuntimeImport}import app, { scheduled, __voidCollectPrerenderPaths, __voidHandleInternalRoute, __voidHandleWebSocket, __withRawEnv${webSocketClasses.length > 0 ? `, ${webSocketClasses.join(", ")}` : ""}${liveRouteImport} } from 'virtual:void-routes';
3743
- ${runtimeEnvImport}${envSchemaImport}${remoteImport}${filterBindingsHelper}${assetHelper}
3755
+ ${runtimeEnvImport}${envSchemaImport}${remoteImport}${isrImport}${filterBindingsHelper}${assetHelper}
3744
3756
  ${internalRouteHelper}
3745
3757
  ${scheduledHandler}
3746
3758
  ${webSocketClasses.map((name) => `export { ${name} };`).join("\n")}
@@ -4362,6 +4374,7 @@ function routingPlugin(root, options) {
4362
4374
  compileOptions.auth.authDatabasePath = databasePath;
4363
4375
  }
4364
4376
  let resolvedConfig = null;
4377
+ let isrRevalidate;
4365
4378
  function resolveAuthClientPath(config) {
4366
4379
  if (config.plugins.some((plugin) => plugin.name === "void-react:pages")) return explicitAuthClientPaths.react;
4367
4380
  if (config.plugins.some((plugin) => plugin.name === "void-vue:pages")) return explicitAuthClientPaths.vue;
@@ -4497,11 +4510,17 @@ function routingPlugin(root, options) {
4497
4510
  if (options.target) {
4498
4511
  writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4499
4512
  writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4500
- } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry(scan.queues.length > 0, remote, resolvedConfig?.build.assetsDir, (scan.websockets ?? []).map((route) => route.className), compileOptions.runtimeEnvPath, getEnvSchemaModule(), options.hasSandbox ? {
4501
- bindingName: options.sandboxBindingName,
4502
- className: options.sandboxClassName,
4503
- runtimePath: sandboxRuntimePath
4504
- } : false, shouldServeSpaFallback(), options.live));
4513
+ } else {
4514
+ const merged = collectRevalidate(readConfig(root), scan.pages);
4515
+ const emitIsr = !options.target && resolvedConfig?.command === "build" && !process.env.VOID_DEPLOY_PROJECT_ID && hasRevalidate(merged);
4516
+ isrRevalidate = emitIsr ? merged : void 0;
4517
+ const isr = emitIsr ? { runtimePath: resolveFile("#self/runtime/isr-cache") } : void 0;
4518
+ writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry(scan.queues.length > 0, remote, resolvedConfig?.build.assetsDir, (scan.websockets ?? []).map((route) => route.className), compileOptions.runtimeEnvPath, getEnvSchemaModule(), options.hasSandbox ? {
4519
+ bindingName: options.sandboxBindingName,
4520
+ className: options.sandboxClassName,
4521
+ runtimePath: sandboxRuntimePath
4522
+ } : false, shouldServeSpaFallback(), options.live, isr));
4523
+ }
4505
4524
  },
4506
4525
  resolveId: {
4507
4526
  filter: { id: RESOLVE_ID_FILTER },
@@ -4525,7 +4544,7 @@ function routingPlugin(root, options) {
4525
4544
  }
4526
4545
  if (id !== VIRTUAL_ROUTES_ID) return;
4527
4546
  if (!scan) await rescan();
4528
- return compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, compileOptions, getPagesCompileOptions());
4547
+ return compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, compileOptions, getPagesCompileOptions(), isrRevalidate);
4529
4548
  }
4530
4549
  },
4531
4550
  configureServer(devServer) {
@@ -4799,6 +4818,20 @@ export default {
4799
4818
  function hasBinding(list, name) {
4800
4819
  return Array.isArray(list) && list.some((b) => b.binding === name);
4801
4820
  }
4821
+ /**
4822
+ * Decide whether to inject a Workers AI binding into the built wrangler config.
4823
+ *
4824
+ * Build-only: dev/miniflare has no local Workers AI, so serve must never bind
4825
+ * `env.AI` (dev uses the P2 `__VOID_TOKEN` proxy path instead).
4826
+ *
4827
+ * `!resolvedAi` is load-bearing: mergeBindings' return is defu-merged over the
4828
+ * resolved root-wrangler config and wins, so skipping when the user already
4829
+ * declared an `ai` binding avoids clobbering theirs. `!resultAi` is harmless
4830
+ * defense-in-depth (nothing else in mergeBindings sets `result.ai`).
4831
+ */
4832
+ function shouldBindWorkersAi(command, needsAI, resolvedAi, resultAi) {
4833
+ return needsAI && command === "build" && !resultAi && !resolvedAi;
4834
+ }
4802
4835
  const CLOUDFLARE_BUILTIN_IMPORT = /^cloudflare:/;
4803
4836
  const SANDBOX_SDK_IMPORT = "@cloudflare/sandbox";
4804
4837
  const SANDBOX_SDK_IMPORT_FILTER = /^@cloudflare\/sandbox$/;
@@ -4916,8 +4949,8 @@ const STAGING_API_URL = "https://api.staging.void.cloud";
4916
4949
  */
4917
4950
  async function runAiSetupPrompt(root, token, staging, server) {
4918
4951
  const { select, text, isCancel, log } = await import("./agents-Bmr5tFFb.mjs").then((n) => n.C);
4919
- const { PlatformClient } = await import("./client-Cj96iiBH.mjs").then((n) => n.r);
4920
- const { writeProjectConfig } = await import("./client-Cj96iiBH.mjs").then((n) => n.y);
4952
+ const { PlatformClient } = await import("./client-C9FG6Vzc.mjs").then((n) => n.r);
4953
+ const { writeProjectConfig } = await import("./client-C9FG6Vzc.mjs").then((n) => n.y);
4921
4954
  const { inferDefaultSlug } = await import("./resolve-project-D2HI3TrG.mjs").then((n) => n.i);
4922
4955
  const client = new PlatformClient(token, staging ? STAGING_API_URL : DEFAULT_API_URL);
4923
4956
  let projects;
@@ -4944,8 +4977,8 @@ async function runAiSetupPrompt(root, token, staging, server) {
4944
4977
  let projectId;
4945
4978
  let slug;
4946
4979
  if (result === "__create__") {
4947
- const { validateProjectSlug } = await import("./create-project-DD9n8Ho-.mjs").then((n) => n.a);
4948
- const { createProjectWithSlugRetry } = await import("./create-project-DD9n8Ho-.mjs").then((n) => n.t);
4980
+ const { validateProjectSlug } = await import("./create-project-CjYX23M_.mjs").then((n) => n.a);
4981
+ const { createProjectWithSlugRetry } = await import("./create-project-CjYX23M_.mjs").then((n) => n.t);
4949
4982
  let created;
4950
4983
  try {
4951
4984
  created = await createProjectWithSlugRetry(client, inferDefaultSlug(root), async (defaultSlug) => {
@@ -5200,6 +5233,12 @@ function voidPlugin(options) {
5200
5233
  binding: r2Name,
5201
5234
  bucket_name: "default"
5202
5235
  }];
5236
+ if (shouldBindWorkersAi(viteCommand, bindings.needsAI, resolved.ai, result.ai)) result.ai = { binding: "AI" };
5237
+ if (config.ai?.gateway) {
5238
+ const vars = result.vars ?? {};
5239
+ vars.__VOID_AI_GATEWAY = config.ai.gateway;
5240
+ result.vars = vars;
5241
+ }
5203
5242
  if (queues.length > 0) result.queues = {
5204
5243
  producers: queues.map((q) => ({
5205
5244
  queue: q.name,
@@ -5579,4 +5618,4 @@ function voidPlugin(options) {
5579
5618
  ];
5580
5619
  }
5581
5620
  //#endregion
5582
- export { VoidAssetRewriteError, basicAuth, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled, voidPlugin };
5621
+ export { VoidAssetRewriteError, basicAuth, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled, shouldBindWorkersAi, voidPlugin };
@@ -1,15 +1,15 @@
1
1
  import { a as join, c as resolve, i as isAbsolute, n as dirname, s as relative, t as basename } from "./pathe.M-eThtNZ-CQzLbt4c.mjs";
2
2
  import { A as text, D as outro, E as note, S as confirm, T as log, a as cliTitle, j as isCancel, k as select, n as getAgentById, r as getPackageDir, t as detectAgents, w as intro, x as cancel } from "./agents-Bmr5tFFb.mjs";
3
3
  import { n as readProjectPaths } from "./project-paths-tpdR1mJR.mjs";
4
- import { S as writeProjectConfig, b as readProjectConfig, f as getToken, m as isStagingMode, n as PlatformClient } from "./client-Cj96iiBH.mjs";
4
+ import { S as writeProjectConfig, b as readProjectConfig, f as getToken, m as isStagingMode, n as PlatformClient } from "./client-C9FG6Vzc.mjs";
5
5
  import { n as ensureProjectTsconfig, t as createProjectTsconfig } from "./project-tsconfig-D9uSVVpA.mjs";
6
- import { c as getDatabaseDialect, f as readConfig } from "./config-1twldYCW.mjs";
6
+ import { c as getDatabaseDialect, f as readConfig } from "./config-BcAeIPe3.mjs";
7
7
  import { r as detectFramework } from "./plugin-inference-CJxi_fWI.mjs";
8
8
  import { r as parseDotenvFile, t as expandDotenv } from "./dotenv-D_UbC_vc.mjs";
9
9
  import { t as MagicString } from "./magic-string.es-ZQjdJFFn.mjs";
10
10
  import { n as writeDrizzleConfig } from "./config-BdUctCZD.mjs";
11
- import { n as promptAndCreateProject, r as promptProjectSelection } from "./create-project-DD9n8Ho-.mjs";
12
- import { t as promptForLoginToken } from "./login-DJReaT_Q.mjs";
11
+ import { n as promptAndCreateProject, r as promptProjectSelection } from "./create-project-CjYX23M_.mjs";
12
+ import { t as promptForLoginToken } from "./login-CrOTnR_6.mjs";
13
13
  import { r as formatProjectCommand } from "./preset-CVvwCeIy.mjs";
14
14
  import { n as findInstalledYarnPnpRuntime, r as hasLocalLockfile, t as buildDrizzleKitSpawnEnv } from "./yarn-pnp-CW8LB6g_.mjs";
15
15
  import { createRequire } from "node:module";
@@ -1,6 +1,6 @@
1
1
  import { D as outro, T as log, a as cliTitle, u as import_picocolors, w as intro, x as cancel } from "./agents-Bmr5tFFb.mjs";
2
- import { S as writeProjectConfig, b as readProjectConfig, d as getSiteDomain, f as getToken, n as PlatformClient } from "./client-Cj96iiBH.mjs";
3
- import { n as promptAndCreateProject, r as promptProjectSelection } from "./create-project-DD9n8Ho-.mjs";
2
+ import { S as writeProjectConfig, b as readProjectConfig, d as getSiteDomain, f as getToken, n as PlatformClient } from "./client-C9FG6Vzc.mjs";
3
+ import { n as promptAndCreateProject, r as promptProjectSelection } from "./create-project-CjYX23M_.mjs";
4
4
  import { r as resolveProjectBySlug } from "./resolve-project-D2HI3TrG.mjs";
5
5
  //#region src/cli/link.ts
6
6
  async function runLink(root, slug) {
@@ -1,5 +1,5 @@
1
1
  import { D as outro, T as log, a as cliTitle, c as ensureUtc, f as tablePrefix, s as dim, u as import_picocolors, w as intro } from "./agents-Bmr5tFFb.mjs";
2
- import { b as readProjectConfig, f as getToken, n as PlatformClient } from "./client-Cj96iiBH.mjs";
2
+ import { b as readProjectConfig, f as getToken, n as PlatformClient } from "./client-C9FG6Vzc.mjs";
3
3
  import { r as resolveProjectBySlug } from "./resolve-project-D2HI3TrG.mjs";
4
4
  //#region src/cli/list.ts
5
5
  async function runListCommand(root, slug) {
@@ -1,5 +1,5 @@
1
1
  import { D as outro, T as log, a as cliTitle, j as isCancel, k as select, o as createSpinner, u as import_picocolors, w as intro, x as cancel } from "./agents-Bmr5tFFb.mjs";
2
- import { _ as runLogin, a as isExpiredTokenError, f as getToken, g as removeToken, m as isStagingMode, n as PlatformClient, p as getTokenSource, v as saveToken } from "./client-Cj96iiBH.mjs";
2
+ import { _ as runLogin, a as isExpiredTokenError, f as getToken, g as removeToken, m as isStagingMode, n as PlatformClient, p as getTokenSource, v as saveToken } from "./client-C9FG6Vzc.mjs";
3
3
  //#region src/cli/login.ts
4
4
  async function promptForLoginToken(root = process.cwd()) {
5
5
  const provider = await select({
@@ -1,5 +1,5 @@
1
1
  import { D as outro, T as log, a as cliTitle, s as dim, u as import_picocolors, w as intro } from "./agents-Bmr5tFFb.mjs";
2
- import { b as readProjectConfig, f as getToken, n as PlatformClient } from "./client-Cj96iiBH.mjs";
2
+ import { b as readProjectConfig, f as getToken, n as PlatformClient } from "./client-C9FG6Vzc.mjs";
3
3
  //#region src/cli/logs.ts
4
4
  async function runLogsCommand(root, filter, range, deployment, level) {
5
5
  console.log();
@@ -1,5 +1,5 @@
1
1
  import { a as join, n as dirname } from "./pathe.M-eThtNZ-CQzLbt4c.mjs";
2
- import { S as writeProjectConfig, f as getToken, n as PlatformClient } from "./client-Cj96iiBH.mjs";
2
+ import { S as writeProjectConfig, f as getToken, n as PlatformClient } from "./client-C9FG6Vzc.mjs";
3
3
  import { existsSync, readFileSync, readdirSync } from "node:fs";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { createInterface } from "node:readline";
@@ -1,6 +1,6 @@
1
1
  import { a as join } from "./pathe.M-eThtNZ-CQzLbt4c.mjs";
2
2
  import { c as loadVoidAuthConfig, s as findVoidAuthConfig } from "./scan-i7Yz54fv.mjs";
3
- import { f as readConfig } from "./config-1twldYCW.mjs";
3
+ import { f as readConfig } from "./config-BcAeIPe3.mjs";
4
4
  import { a as resolveVoidAuthMigrationSchema } from "./route-types-CfKfhbIg.mjs";
5
5
  import { n as applySqliteAuthMigrations, t as applyPgAuthMigrations } from "./auth-migrations-BTZ-ATvQ.mjs";
6
6
  import { getMiniflareDatabasePath } from "./runner-h272wcPj.mjs";
@@ -1,8 +1,8 @@
1
1
  import { a as join } from "../pathe.M-eThtNZ-CQzLbt4c.mjs";
2
2
  import { r as resolvePagesDir } from "../project-paths-tpdR1mJR.mjs";
3
- import { f as readConfig } from "../config-1twldYCW.mjs";
3
+ import { f as readConfig } from "../config-BcAeIPe3.mjs";
4
4
  import { a as resolveNamedLayout, i as resolveLayoutIds, n as resolveEffectiveLayoutChain, r as resolveLayoutChain, t as scanPages } from "../scan-Dp_Gyzs3.mjs";
5
- import { i as parseRedirectsFile } from "../headers-BwvFGhkx.mjs";
5
+ import { i as parseRedirectsFile } from "../headers-CTAjX-UO.mjs";
6
6
  import { renderBodyAttrs, renderHeadToString, renderHtmlAttrs, resolveHead } from "./head.mjs";
7
7
  import { serializePageData } from "./serialize.mjs";
8
8
  //#region src/pages/manifest.ts
@@ -3,7 +3,7 @@ import { D as outro, E as note, T as log, a as cliTitle, w as intro } from "./ag
3
3
  import { i as resolveProjectPaths } from "./project-paths-tpdR1mJR.mjs";
4
4
  import { c as loadVoidAuthConfig, r as scanRoutes, s as findVoidAuthConfig } from "./scan-i7Yz54fv.mjs";
5
5
  import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-D9uSVVpA.mjs";
6
- import { c as getDatabaseDialect, f as readConfig } from "./config-1twldYCW.mjs";
6
+ import { c as getDatabaseDialect, f as readConfig } from "./config-BcAeIPe3.mjs";
7
7
  import { i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, r as detectFramework } from "./plugin-inference-CJxi_fWI.mjs";
8
8
  import { i as generateVoidAuthDrizzleSchema, n as generateQueueTypes, r as generateVoidAuthDatabaseModule, s as generateDbDts, t as generateRouteTypes } from "./route-types-CfKfhbIg.mjs";
9
9
  import { t as discoverSchema } from "./discover-BuVVSAum.mjs";
@@ -1,5 +1,5 @@
1
1
  import { D as outro, T as log, a as cliTitle, f as tablePrefix, s as dim, u as import_picocolors, w as intro } from "./agents-Bmr5tFFb.mjs";
2
- import { d as getSiteDomain, f as getToken, n as PlatformClient } from "./client-Cj96iiBH.mjs";
2
+ import { d as getSiteDomain, f as getToken, n as PlatformClient } from "./client-C9FG6Vzc.mjs";
3
3
  //#region src/cli/projects.ts
4
4
  async function runProjectsCommand(root) {
5
5
  console.log();
@@ -33,32 +33,32 @@ async function runProjectsCommand(root) {
33
33
  //#region src/cli/project-cmd.ts
34
34
  async function runProjectCommand(root, args) {
35
35
  if (args.subcommand === "status") {
36
- const { runListCommand } = await import("./list-3GEw7b6m.mjs");
36
+ const { runListCommand } = await import("./list-BKDjA3M5.mjs");
37
37
  return runListCommand(root, args.name || void 0);
38
38
  }
39
39
  if (args.subcommand === "link") {
40
- const { runLink } = await import("./link-CUmiosyb.mjs");
40
+ const { runLink } = await import("./link-Co136MEs.mjs");
41
41
  return runLink(root, args.name || void 0);
42
42
  }
43
43
  if (args.subcommand === "list") return runProjectsCommand(root);
44
44
  if (args.subcommand === "logs") {
45
- const { runLogsCommand } = await import("./logs-D-rQ56Lq.mjs");
45
+ const { runLogsCommand } = await import("./logs-DlAg25ww.mjs");
46
46
  return runLogsCommand(root, args.filter, args.range, args.deployment || void 0, args.level || void 0);
47
47
  }
48
48
  if (args.subcommand === "rollback") {
49
- const { runRollbackCommand } = await import("./rollback-Yh7bCKob.mjs");
49
+ const { runRollbackCommand } = await import("./rollback-9dfmB-YZ.mjs");
50
50
  return runRollbackCommand(root, args.deployId || void 0);
51
51
  }
52
52
  if (args.subcommand === "cancel") {
53
- const { runCancelDeployCommand } = await import("./cancel-deploy-BEBOEgtu.mjs");
53
+ const { runCancelDeployCommand } = await import("./cancel-deploy-BosnzEHC.mjs");
54
54
  return runCancelDeployCommand(root, args.deployId || void 0);
55
55
  }
56
56
  if (args.subcommand === "delete") {
57
- const { runDeleteCommand } = await import("./delete-D2kr3Kmk.mjs");
57
+ const { runDeleteCommand } = await import("./delete-CThKLXfi.mjs");
58
58
  return runDeleteCommand(root, args.name || void 0, { force: args.force });
59
59
  }
60
60
  if (args.subcommand === "purge-cache") {
61
- const { runPurgeCacheCommand } = await import("./cache-wH-mP8UE.mjs");
61
+ const { runPurgeCacheCommand } = await import("./cache-DIUSnIjJ.mjs");
62
62
  return runPurgeCacheCommand(root, args.projectSlug || void 0);
63
63
  }
64
64
  }
@@ -1,5 +1,5 @@
1
1
  import { D as outro, S as confirm, T as log, a as cliTitle, c as ensureUtc, j as isCancel, k as select, o as createSpinner, s as dim, u as import_picocolors, w as intro, x as cancel } from "./agents-Bmr5tFFb.mjs";
2
- import { b as readProjectConfig, f as getToken, n as PlatformClient } from "./client-Cj96iiBH.mjs";
2
+ import { b as readProjectConfig, f as getToken, n as PlatformClient } from "./client-C9FG6Vzc.mjs";
3
3
  //#region src/cli/rollback.ts
4
4
  async function runRollbackCommand(root, deployId) {
5
5
  console.log();
@@ -1,5 +1,5 @@
1
1
  import { t as cfAccessHeaders } from "../cf-access-Bqw81xAf.mjs";
2
- import { i as getRawRuntimeEnv } from "../env-raw-CoS20LHP.mjs";
2
+ import { a as getRawRuntimeEnv } from "../env-raw-Cx8ElDdj.mjs";
3
3
  //#region src/runtime/ai-types.ts
4
4
  /** Maps provider prefix to expected env var name for the API key */
5
5
  const PROVIDER_KEY_MAP = {
@@ -29,6 +29,44 @@ const PROVIDER_KEY_MAP = {
29
29
  const PROXY_URL = "https://proxy.void.cloud/ai";
30
30
  const STAGING_PROXY_URL = "https://proxy.staging.void.cloud/ai";
31
31
  const VOID_FILE_MARKER = "__voidFile";
32
+ const PROVIDER_AUTH_DEFAULTS = {
33
+ "google-ai-studio": {
34
+ header: "x-goog-api-key",
35
+ prefix: ""
36
+ },
37
+ anthropic: {
38
+ header: "x-api-key",
39
+ prefix: ""
40
+ },
41
+ elevenlabs: {
42
+ header: "xi-api-key",
43
+ prefix: ""
44
+ },
45
+ deepgram: {
46
+ header: "authorization",
47
+ prefix: "Token "
48
+ },
49
+ fal: {
50
+ header: "authorization",
51
+ prefix: "Key "
52
+ },
53
+ cartesia: {
54
+ header: "x-api-key",
55
+ prefix: ""
56
+ },
57
+ baseten: {
58
+ header: "authorization",
59
+ prefix: "Api-Key "
60
+ },
61
+ ideogram: {
62
+ header: "Api-Key",
63
+ prefix: ""
64
+ },
65
+ parallel: {
66
+ header: "x-api-key",
67
+ prefix: ""
68
+ }
69
+ };
32
70
  function hasStreamFlag(value) {
33
71
  return typeof value === "object" && value !== null && "stream" in value;
34
72
  }
@@ -203,6 +241,41 @@ function getProxyUrl(runtimeEnv) {
203
241
  if (typeof custom === "string" && custom.length > 0) return custom + "/ai";
204
242
  return runtimeEnv.__VOID_STAGING ? STAGING_PROXY_URL : PROXY_URL;
205
243
  }
244
+ function providerAuthDefaults(provider) {
245
+ return PROVIDER_AUTH_DEFAULTS[provider] ?? {
246
+ header: "authorization",
247
+ prefix: "Bearer "
248
+ };
249
+ }
250
+ function gatewayEndpointUrl(baseUrl, endpoint) {
251
+ return `${baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl}/${endpoint.startsWith("/") ? endpoint.slice(1) : endpoint}`;
252
+ }
253
+ function isUnsafeProviderPath(path) {
254
+ if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(path) || path.includes("\\")) return true;
255
+ const pathname = path.split(/[?#]/, 1)[0] ?? "";
256
+ for (const segment of pathname.split("/")) try {
257
+ if (decodeURIComponent(segment) === "..") return true;
258
+ } catch {
259
+ return true;
260
+ }
261
+ return false;
262
+ }
263
+ /**
264
+ * Build a real outgoing HTTP body for the user's own AI Gateway (P3 self-host).
265
+ *
266
+ * Unlike `serializeProviderBody`/`providerRequestBody` (which build the proxy
267
+ * envelope decoded on the proxy side), this emits an actual `BodyInit` +
268
+ * content-type for a direct `fetch` to the gateway.
269
+ */
270
+ function toDirectBody(body) {
271
+ if (body === void 0 || body === null) return { body: void 0 };
272
+ if (typeof body === "string") return { body };
273
+ if (body instanceof ReadableStream || body instanceof FormData || body instanceof URLSearchParams || body instanceof Blob || body instanceof ArrayBuffer || isBinaryView(body)) return { body };
274
+ return {
275
+ body: JSON.stringify(body),
276
+ contentType: "application/json"
277
+ };
278
+ }
206
279
  function resolveBackend() {
207
280
  const runtimeEnv = getRawRuntimeEnv();
208
281
  const proxy = runtimeEnv.__VOID_PROXY;
@@ -211,26 +284,14 @@ function resolveBackend() {
211
284
  const projectId = runtimeEnv.__VOID_PROJECT_ID;
212
285
  if (typeof token === "string" && typeof projectId === "string") return makeHttpBackend(token, projectId, runtimeEnv);
213
286
  const directAi = runtimeEnv.AI;
214
- if (directAi) return {
215
- run: directAi.run.bind(directAi),
216
- async image(model, inputs, options) {
217
- const { runOptions } = splitImageOptions(options);
218
- return responseFromResult(await directAi.run(model, inputs, runOptions));
219
- },
220
- providerFetch: unsupportedProviderFetch,
221
- models: directAi.models.bind(directAi),
222
- toMarkdown: directAi.toMarkdown?.bind(directAi) ?? unsupported("toMarkdown")
223
- };
224
- throw new Error("ai: Not available. Run `void auth login` and `void project link` first.");
287
+ if (directAi) return makeDirectBackend(directAi, runtimeEnv, runtimeEnv.__VOID_AI_GATEWAY);
288
+ throw new Error("ai: Not available. Run `void auth login` and `void project link` first, or bind Workers AI (env.AI) for self-hosted deploys.");
225
289
  }
226
290
  function unsupported(method) {
227
291
  return () => {
228
292
  throw new Error(`ai: ai.${method}() is not supported in this environment.`);
229
293
  };
230
294
  }
231
- function unsupportedProviderFetch() {
232
- throw new Error("ai: ai.provider().fetch() is not supported in this environment.");
233
- }
234
295
  async function providerRequestBody(provider, path, init, options, runtimeEnv) {
235
296
  return {
236
297
  provider,
@@ -370,6 +431,43 @@ function makeHttpBackend(token, projectId, runtimeEnv) {
370
431
  }
371
432
  };
372
433
  }
434
+ function makeDirectBackend(directAi, runtimeEnv, gatewayId) {
435
+ return {
436
+ run: directAi.run.bind(directAi),
437
+ async image(model, inputs, options) {
438
+ const { runOptions } = splitImageOptions(options);
439
+ return responseFromResult(await directAi.run(model, inputs, runOptions));
440
+ },
441
+ async providerFetch(provider, path, init, options) {
442
+ if (!gatewayId) return Response.json({ error: "ai: ai.provider().fetch() needs an AI Gateway when self-hosting. Set \"ai\": { \"gateway\": \"<id>\" } in void.json." }, { status: 501 });
443
+ if (isUnsafeProviderPath(path)) return Response.json({ error: `ai: provider path '${path}' must be relative.` }, { status: 400 });
444
+ let providerKey;
445
+ try {
446
+ providerKey = resolveProviderKey(provider, runtimeEnv, options);
447
+ } catch (e) {
448
+ return Response.json({ error: e.message }, { status: 400 });
449
+ }
450
+ let baseUrl;
451
+ try {
452
+ baseUrl = await directAi.gateway(gatewayId).getUrl(provider);
453
+ } catch (e) {
454
+ return Response.json({ error: `ai: gateway '${gatewayId}': ${e.message}` }, { status: 502 });
455
+ }
456
+ const { body, contentType } = toDirectBody(init?.body);
457
+ const headers = new Headers(init?.headers);
458
+ const defaults = providerAuthDefaults(provider);
459
+ headers.set(options?.apiKeyHeader ?? defaults.header, `${options?.apiKeyPrefix ?? defaults.prefix}${providerKey}`);
460
+ if (contentType && !headers.has("content-type")) headers.set("content-type", contentType);
461
+ return fetch(gatewayEndpointUrl(baseUrl, path), {
462
+ method: init?.method ?? "POST",
463
+ headers,
464
+ body
465
+ });
466
+ },
467
+ models: directAi.models.bind(directAi),
468
+ toMarkdown: directAi.toMarkdown?.bind(directAi) ?? unsupported("toMarkdown")
469
+ };
470
+ }
373
471
  let _backend;
374
472
  function backend() {
375
473
  return _backend ??= resolveBackend();