Ctrl K
ComunidadGuías…

vite.config.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { assertNodeVersion } from './scripts/lib/node-version.mjs';
import { cloudflare, type WorkerConfig } from '@cloudflare/vite-plugin';
import { flue, flueWorkerConfig } from '@flue/vite';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import { defineConfig, type UserConfig } from 'vite';
import { fileURLToPath } from 'node:url';
import { buildIdentityDefines, readBuildIdentity } from './scripts/lib/build-identity.mjs';
import { bundleModuleReport } from './scripts/lib/bundle-module-report.mjs';

import { applyCloudflareDeploymentProfile } from './scripts/cloudflare-deployment-profile.mjs';
import { localWorkerViteSettings } from './scripts/lib/local-worker-lane.mjs';
import { PUBLIC_ASSET_PATHS } from './src/assets/public-assets.ts';

const projectRoot = fileURLToPath(new URL('.', import.meta.url));

/**
 * A host that builds this Worker from an unmodified checkout of this
 * repository, supplying its own entries and output. Without a host, the
 * config is this repository's own build and local Worker lanes.
 */
export interface ChickpeaWorkerHost {
  /** Absolute path of the host's app entry, used instead of src/app.ts. */
  app: string;
  /** Absolute path of the host's non-HTTP handlers entry, used instead of src/cloudflare.ts. */
  cloudflare: string;
  /** Absolute build output directory, outside this checkout. */
  outDir: string;
  /** Adjusts the Worker config after Flue and Chickpea have built it. */
  configureWorker?: (config: WorkerConfig) => void;
}

assertNodeVersion();

export function chickpeaWorkerViteConfig(command: 'build' | 'serve', host?: ChickpeaWorkerHost): UserConfig {
  const fluePlugins = flue({
    providers: ['anthropic', 'openai', 'openrouter', 'google', 'cloudflare'],
    tracing: false,
    ...(host ? { app: host.app, cloudflare: host.cloudflare } : {}),
  });
  // Capture the customizer from the same flue() instance during config
  // evaluation, then compose the optional deployment overlay after Flue has
  // added its generated entry and Durable Object bindings. Flue must run
  // first: its project and agent scan feeds the Cloudflare plugin's generated
  // Worker configuration during the same config pass.
  const configureFlueWorker = flueWorkerConfig();
  // Local Worker lanes belong to this repository's own development.
  const local = command === 'serve' && !host ? localWorkerViteSettings() : undefined;
  return {
    // Explicit so a host running Vite from its own directory still resolves
    // this project, its source root and its Worker config.
    root: projectRoot,
    define: {
      ...buildIdentityDefines(projectRoot),
      // Explicit build-time mode: every Vite serve lane is local development,
      // where the committed build identity does not track working-tree schema
      // edits. Local serve must never attach to a persisted schema marker.
      __CHICKPEA_VITE_SERVE__: JSON.stringify(command === 'serve'),
      // How this Worker was built, so Admin can show matching redeploy steps.
      // Cloudflare Workers Builds sets WORKERS_CI=1; a local or generic-CI
      // `npm run deploy` does not. A serve lane is neither.
      __CHICKPEA_CLOUDFLARE_BUILD_SOURCE__: JSON.stringify(
        command === 'serve' ? 'unknown' : process.env.WORKERS_CI === '1' ? 'workers-builds' : 'command',
      ),
    },
    // Public images are uploaded as Static Assets, not embedded in Worker code.
    publicDir: 'assets',
    plugins: [
      fluePlugins,
      {
        name: 'chickpea-public-assets',
        apply: 'build',
        async generateBundle() {
          if (this.environment.name !== 'client') return;
          // Keep source/README artwork in the repository, but publish only the
          // images the application uses. publicDir still supports local dev.
          for (const fileName of PUBLIC_ASSET_PATHS) {
            this.emitFile({
              type: 'asset',
              fileName,
              source: await readFile(path.resolve(this.environment.config.publicDir, fileName)),
            });
          }
        },
      },
      bundleModuleReport(),
      cloudflare({
        ...(local ? {
          persistState: { path: local.statePath },
          tunnel: { name: local.tunnelName, autoStart: true },
        } : {}),
        config(config) {
          configureFlueWorker(config);
          applyCloudflareDeploymentProfile(config);
          const identity = readBuildIdentity(projectRoot);
          config.vars = {
            ...(config.vars ?? {}),
            CHICKPEA_APP_VERSION: identity.version,
            CHICKPEA_SOURCE_COMMIT: identity.sourceCommit ?? 'unknown',
          };
          if (local) {
            const authDb = config.d1_databases?.find((database) => database.binding === 'AUTH_DB');
            if (!authDb) throw new Error('Local Worker development requires the AUTH_DB binding.');
            authDb.database_id = local.d1DatabaseId;
            config.assets = {
              ...(config.assets ?? {}),
              // Static assets normally run before the Worker. Agent avatars
              // are generated from local state, so let the dynamic route own
              // only that namespace while keeping the static asset binding.
              run_worker_first: ['/assets/agents/*'],
            };
            config.ai = { ...(config.ai ?? { binding: 'AI' }), remote: true };
            config.vars = {
              ...(config.vars ?? {}),
              CHICKPEA_LOCAL_LANE: local.lane,
              CHICKPEA_LOCAL_RUNTIME: 'cloudflare-vite/workerd',
              CHICKPEA_LOCAL_TRANSPORT: 'slack-http-events',
              CHICKPEA_LOCAL_SOURCE_SHA: local.sourceSha,
              SLACK_TAG_PUBLIC_URL: local.publicUrl,
              CHICKPEA_TELEMETRY_ENVIRONMENT: 'development',
            };
          }
          host?.configureWorker?.(config);
        }
      }),
    ],
    resolve: {
      alias: [
        // Never reached on Chickpea's paths; see src/build-stubs/empty-module.ts.
        { find: /^mimetext$/, replacement: path.join(projectRoot, 'src/build-stubs/empty-module.ts') },
        { find: /^pusher-js$/, replacement: path.join(projectRoot, 'src/build-stubs/empty-module.ts') },
      ],
    },
    build: {
      outDir: host?.outDir ?? 'dist-cf',
      // Vite empties only an output directory inside the root; a host's is outside it.
      ...(host ? { emptyOutDir: true } : {}),
      copyPublicDir: false,
      minify: 'oxc',
      // Better Auth dispatches on error.name, which jose derives from the class
      // name; without this, revoking an expired access token answers 500.
      rolldownOptions: { output: { keepNames: true } },
    },
    ...(local ? {
      optimizeDeps: {
        // This Worker graph is ESM. Discovery repeatedly invalidates pi-ai's
        // generated provider chunks during workerd boot, so run the source
        // graph directly instead of maintaining a disposable prebundle cache.
        noDiscovery: true,
        include: [],
        exclude: ['@earendil-works/pi-ai'],
      },
      server: {
        host: '127.0.0.1',
        port: local.port,
        strictPort: true,
        allowedHosts: [
          new URL(local.publicUrl).hostname,
          // read.cloudflare.ts uses this non-routable origin when it calls the
          // ASSETS binding from Durable Objects and other requestless paths.
          'chickpea-assets.invalid',
        ],
      },
    } : {}),
  };
}

export default defineConfig(({ command }) => chickpeaWorkerViteConfig(command));