devflare 1.0.0-next.2 → 1.0.0-next.3

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.
@@ -20,14 +20,13 @@ export declare function createEnvProxy(options?: EnvProxyOptions & {
20
20
  /**
21
21
  * Get the global env proxy for bridge RPC
22
22
  *
23
- * Note: This is distinct from `devflare/runtime`'s `env` which provides
24
- * request-scoped context access. Use `bridgeEnv` for standalone bridge usage
25
- * (e.g., testing, scripts) and the runtime `env` within request handlers.
23
+ * Note: This is distinct from the published `import { env } from 'devflare'`
24
+ * proxy, which provides unified request/test/bridge-aware access.
25
+ * Use `bridgeEnv` for standalone internal bridge usage and `env` from the
26
+ * main package within request handlers and normal test flows.
26
27
  *
27
28
  * @example
28
29
  * ```ts
29
- * import { bridgeEnv } from 'devflare'
30
- *
31
30
  * await bridgeEnv.MY_KV.get('key')
32
31
  * await bridgeEnv.MY_DO.get(id).fetch(request)
33
32
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/bridge/proxy.ts"],"names":[],"mappings":"AAMA,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,UAAU,CAAA;AAYvD,MAAM,WAAW,eAAe;IAC/B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,qCAAqC;IACrC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,wEAAwE;IACxE,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAA;CAC9C;AA0aD,mDAAmD;AACnD,MAAM,WAAW,YAAY;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAA;CACxF;AAKD;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,eAAe,GAAG;IAAE,KAAK,CAAC,EAAE,YAAY,CAAA;CAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsEhH;AAqED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAO5C,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,GAAG,yBAAY,CAAA;AAE5B;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,eAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAG9E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAIzD"}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/bridge/proxy.ts"],"names":[],"mappings":"AAMA,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,UAAU,CAAA;AAYvD,MAAM,WAAW,eAAe;IAC/B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,qCAAqC;IACrC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,wEAAwE;IACxE,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAA;CAC9C;AA0aD,mDAAmD;AACnD,MAAM,WAAW,YAAY;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAA;CACxF;AAKD;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,eAAe,GAAG;IAAE,KAAK,CAAC,EAAE,YAAY,CAAA;CAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsEhH;AAqED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAO5C,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,GAAG,yBAAY,CAAA;AAE5B;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,eAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAG9E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAIzD"}
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dev.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,SAAS,CAAA;AAE7D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AA+DjE,wBAAsB,aAAa,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,UAAU,GACjB,OAAO,CAAC,SAAS,CAAC,CAgGpB"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dev.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,SAAS,CAAA;AAE7D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAgEjE,wBAAsB,aAAa,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,UAAU,GACjB,OAAO,CAAC,SAAS,CAAC,CA4JpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAUjE,wBAAsB,gBAAgB,CACrC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,UAAU,GACjB,OAAO,CAAC,SAAS,CAAC,CAqMpB"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAWjE,wBAAsB,gBAAgB,CACrC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,UAAU,GACjB,OAAO,CAAC,SAAS,CAAC,CAoMpB"}
@@ -1,3 +1,8 @@
1
+ import {
2
+ detectViteProject,
3
+ stopSpawnedProcessTree,
4
+ waitForViteReady
5
+ } from "./index-18hvb6gb.js";
1
6
  import {
2
7
  findFiles
3
8
  } from "./index-rbht7m9r.js";
@@ -1663,6 +1668,7 @@ function createDevServer(options) {
1663
1668
  configPath,
1664
1669
  vitePort = 5173,
1665
1670
  miniflarePort = 8787,
1671
+ enableVite = true,
1666
1672
  persist = true,
1667
1673
  logger,
1668
1674
  verbose = false,
@@ -1878,7 +1884,8 @@ function createDevServer(options) {
1878
1884
  const args = ["vite", "dev", "--port", String(vitePort)];
1879
1885
  viteProcess = spawn("bunx", args, {
1880
1886
  cwd,
1881
- stdio: "inherit",
1887
+ stdio: ["inherit", "pipe", "pipe"],
1888
+ windowsHide: true,
1882
1889
  env: {
1883
1890
  ...process.env,
1884
1891
  DEVFLARE_DEV: "true",
@@ -1886,7 +1893,19 @@ function createDevServer(options) {
1886
1893
  FORCE_COLOR: "1"
1887
1894
  }
1888
1895
  });
1889
- logger?.success(`Vite dev server started on http://localhost:${vitePort}`);
1896
+ const readyUrl = await waitForViteReady(viteProcess, {
1897
+ onStdout(chunk) {
1898
+ process.stdout.write(chunk);
1899
+ },
1900
+ onStderr(chunk) {
1901
+ process.stderr.write(chunk);
1902
+ }
1903
+ });
1904
+ if (readyUrl) {
1905
+ logger?.success(`Vite dev server started on ${readyUrl}`);
1906
+ return;
1907
+ }
1908
+ logger?.warn("Vite process started, but the final local URL could not be confirmed yet");
1890
1909
  }
1891
1910
  async function start() {
1892
1911
  logger?.info("Starting unified dev server...");
@@ -1945,16 +1964,13 @@ function createDevServer(options) {
1945
1964
  await doBundler.watch();
1946
1965
  }
1947
1966
  await startMiniflare(doResult);
1948
- await startVite();
1967
+ if (enableVite) {
1968
+ await startVite();
1969
+ } else {
1970
+ logger?.info("Vite startup skipped (no local vite.config.* found for this package)");
1971
+ }
1949
1972
  await new Promise((r) => setTimeout(r, 1000));
1950
1973
  await runD1Migrations();
1951
- const cleanup = async () => {
1952
- logger?.info("Shutting down...");
1953
- await stop();
1954
- process.exit(0);
1955
- };
1956
- process.on("SIGINT", cleanup);
1957
- process.on("SIGTERM", cleanup);
1958
1974
  }
1959
1975
  async function stop() {
1960
1976
  if (doBundler) {
@@ -1966,7 +1982,7 @@ function createDevServer(options) {
1966
1982
  miniflare = null;
1967
1983
  }
1968
1984
  if (viteProcess) {
1969
- viteProcess.kill("SIGTERM");
1985
+ await stopSpawnedProcessTree(viteProcess);
1970
1986
  viteProcess = null;
1971
1987
  }
1972
1988
  if (browserShim) {
@@ -2025,6 +2041,7 @@ async function runDevCommand(parsed, logger, options) {
2025
2041
  const persistEnabled = parsed.options.persist === true;
2026
2042
  const debugEnabled = parsed.options.debug === true || process.env.DEVFLARE_DEBUG === "true";
2027
2043
  const verbose = parsed.options.verbose === true || debugEnabled;
2044
+ const viteProject = await detectViteProject(cwd);
2028
2045
  const logWriter = await createLogWriter(cwd, {
2029
2046
  log: logEnabled,
2030
2047
  logTemp: logTempEnabled
@@ -2052,42 +2069,91 @@ async function runDevCommand(parsed, logger, options) {
2052
2069
  }
2053
2070
  try {
2054
2071
  logger.info("");
2055
- logger.info("\uD83D\uDE80 Devflare Unified Dev Server");
2056
- logger.info(" ├─ Vite: Full HMR for frontend");
2057
- logger.info(" ├─ Miniflare: All Cloudflare bindings");
2058
- logger.info(" ├─ Rolldown: Fast DO bundling with watch");
2059
- logger.info(" └─ Bridge: WebSocket RPC connection");
2072
+ if (viteProject.shouldStartVite) {
2073
+ logger.info("\uD83D\uDE80 Devflare Unified Dev Server");
2074
+ logger.info(" ├─ Vite: Full HMR for frontend");
2075
+ logger.info(" ├─ Miniflare: All Cloudflare bindings");
2076
+ logger.info(" ├─ Rolldown: Fast DO bundling with watch");
2077
+ logger.info(" └─ Bridge: WebSocket RPC connection");
2078
+ } else {
2079
+ logger.info("\uD83D\uDE80 Devflare Worker Dev Server");
2080
+ logger.info(" ├─ Miniflare: All Cloudflare bindings");
2081
+ logger.info(" ├─ Rolldown: Fast DO bundling with watch");
2082
+ logger.info(" └─ Vite: Disabled (no local vite.config.* found)");
2083
+ if (viteProject.wantsViteIntegration) {
2084
+ logger.warn("Vite-related dependencies were found, but no local vite.config.* was detected");
2085
+ logger.warn("Skipping Vite startup and running in worker-only mode");
2086
+ }
2087
+ }
2060
2088
  logger.info("");
2061
2089
  const devServer = createDevServer({
2062
2090
  cwd,
2063
2091
  configPath,
2064
2092
  vitePort: port ? parseInt(port, 10) : 5173,
2065
2093
  miniflarePort: 8787,
2094
+ enableVite: viteProject.shouldStartVite,
2066
2095
  persist: persistEnabled,
2067
2096
  logger: devLogger,
2068
2097
  verbose,
2069
2098
  debug: debugEnabled
2070
2099
  });
2071
- const cleanup = async () => {
2100
+ let isCleaningUp = false;
2101
+ const cleanupHandlers = new Map;
2102
+ const removeCleanupHandlers = () => {
2103
+ for (const [event, handler] of cleanupHandlers) {
2104
+ process.off(event, handler);
2105
+ }
2106
+ cleanupHandlers.clear();
2107
+ };
2108
+ const cleanup = async (exitCode, reason) => {
2109
+ if (isCleaningUp) {
2110
+ return;
2111
+ }
2112
+ isCleaningUp = true;
2113
+ removeCleanupHandlers();
2114
+ if (reason) {
2115
+ const message = reason instanceof Error ? reason.stack ?? reason.message : String(reason);
2116
+ logger.error(message);
2117
+ }
2072
2118
  logger.info("");
2073
2119
  logger.info("Shutting down...");
2074
- await devServer.stop();
2075
- logWriter?.close();
2076
- process.exit(0);
2120
+ try {
2121
+ await devServer.stop();
2122
+ } finally {
2123
+ logWriter?.close();
2124
+ process.exit(exitCode);
2125
+ }
2126
+ };
2127
+ const registerCleanupHandler = (event, handler) => {
2128
+ cleanupHandlers.set(event, handler);
2129
+ process.on(event, handler);
2077
2130
  };
2078
- process.on("SIGINT", cleanup);
2079
- process.on("SIGTERM", cleanup);
2131
+ registerCleanupHandler("SIGINT", () => {
2132
+ cleanup(0);
2133
+ });
2134
+ registerCleanupHandler("SIGTERM", () => {
2135
+ cleanup(0);
2136
+ });
2137
+ registerCleanupHandler("SIGHUP", () => {
2138
+ cleanup(0);
2139
+ });
2140
+ registerCleanupHandler("uncaughtException", (error) => {
2141
+ cleanup(1, error);
2142
+ });
2143
+ registerCleanupHandler("unhandledRejection", (reason) => {
2144
+ cleanup(1, reason);
2145
+ });
2080
2146
  await devServer.start();
2081
2147
  await new Promise(() => {});
2082
2148
  return { exitCode: 0 };
2083
2149
  } catch (error) {
2150
+ logWriter?.close();
2084
2151
  if (error instanceof Error) {
2085
2152
  logger.error("Dev server failed:", error.message);
2086
2153
  if (verbose) {
2087
2154
  logger.error(error.stack);
2088
2155
  }
2089
2156
  }
2090
- logWriter?.close();
2091
2157
  return { exitCode: 1 };
2092
2158
  }
2093
2159
  }
@@ -9,6 +9,8 @@ export interface DevServerOptions {
9
9
  vitePort?: number;
10
10
  /** Miniflare port for gateway (default: 8787) */
11
11
  miniflarePort?: number;
12
+ /** Whether to start Vite for this package */
13
+ enableVite?: boolean;
12
14
  /** Persist storage data */
13
15
  persist?: boolean;
14
16
  /** Logger instance */
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dev-server/server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,WAAW,CAAA;AAa3D,MAAM,WAAW,gBAAgB;IAChC,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sBAAsB;IACtB,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,SAAS;IACzB,2BAA2B;IAC3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,0BAA0B;IAC1B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,yCAAyC;IACzC,YAAY,IAAI,aAAa,GAAG,IAAI,CAAA;CACpC;AA2kBD,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAmepE"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dev-server/server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,WAAW,CAAA;AAc3D,MAAM,WAAW,gBAAgB;IAChC,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sBAAsB;IACtB,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,SAAS;IACzB,2BAA2B;IAC3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,0BAA0B;IAC1B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,yCAAyC;IACzC,YAAY,IAAI,aAAa,GAAG,IAAI,CAAA;CACpC;AA2kBD,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CA4epE"}
@@ -0,0 +1,37 @@
1
+ export declare const VITE_CONFIG_FILES: readonly ["vite.config.ts", "vite.config.js", "vite.config.mts", "vite.config.mjs"];
2
+ export interface ViteProjectFileSystem {
3
+ access(path: string): Promise<void>;
4
+ readFile(path: string, encoding: BufferEncoding): Promise<string>;
5
+ }
6
+ export interface ViteProjectDetection {
7
+ viteConfigPath: string | null;
8
+ hasLocalViteDependency: boolean;
9
+ hasLocalCloudflareVitePluginDependency: boolean;
10
+ shouldStartVite: boolean;
11
+ wantsViteIntegration: boolean;
12
+ }
13
+ export interface SpawnedLikeProcess {
14
+ pid?: number;
15
+ stdout: NodeJS.ReadableStream | null;
16
+ stderr: NodeJS.ReadableStream | null;
17
+ readonly killed: boolean;
18
+ kill(signal?: NodeJS.Signals): boolean;
19
+ on(event: 'exit', handler: (code: number | null, signal: NodeJS.Signals | null) => void): SpawnedLikeProcess;
20
+ on(event: 'error', handler: (error: Error) => void): SpawnedLikeProcess;
21
+ }
22
+ export interface WaitForViteReadyOptions {
23
+ timeoutMs?: number;
24
+ onStdout?: (chunk: string | Buffer) => void;
25
+ onStderr?: (chunk: string | Buffer) => void;
26
+ }
27
+ export interface StopProcessTreeOptions {
28
+ platform?: NodeJS.Platform;
29
+ timeoutMs?: number;
30
+ runCommand?: (command: string, args: string[]) => Promise<void>;
31
+ }
32
+ export declare function detectViteProject(cwd: string, fs?: ViteProjectFileSystem): Promise<ViteProjectDetection>;
33
+ export declare function stripAnsi(value: string): string;
34
+ export declare function extractViteReadyUrl(output: string): string | null;
35
+ export declare function waitForViteReady(process: SpawnedLikeProcess, options?: WaitForViteReadyOptions): Promise<string | null>;
36
+ export declare function stopSpawnedProcessTree(process: Pick<SpawnedLikeProcess, 'pid' | 'kill' | 'killed' | 'on'>, options?: StopProcessTreeOptions): Promise<void>;
37
+ //# sourceMappingURL=vite-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-utils.d.ts","sourceRoot":"","sources":["../../src/dev-server/vite-utils.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,qFAKpB,CAAA;AAEV,MAAM,WAAW,qBAAqB;IACrC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACjE;AAED,MAAM,WAAW,oBAAoB;IACpC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,sBAAsB,EAAE,OAAO,CAAA;IAC/B,sCAAsC,EAAE,OAAO,CAAA;IAC/C,eAAe,EAAE,OAAO,CAAA;IACxB,oBAAoB,EAAE,OAAO,CAAA;CAC7B;AAED,MAAM,WAAW,kBAAkB;IAClC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAC,cAAc,GAAG,IAAI,CAAA;IACpC,MAAM,EAAE,MAAM,CAAC,cAAc,GAAG,IAAI,CAAA;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAA;IACtC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,KAAK,IAAI,GAAG,kBAAkB,CAAA;IAC5G,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,kBAAkB,CAAA;CACvE;AAED,MAAM,WAAW,uBAAuB;IACvC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAA;IAC3C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAA;CAC3C;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/D;AAwBD,wBAAsB,iBAAiB,CACtC,GAAG,EAAE,MAAM,EACX,EAAE,CAAC,EAAE,qBAAqB,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAoC/B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoBjE;AAED,wBAAsB,gBAAgB,CACrC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,GAAE,uBAA4B,GACnC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuDxB;AA2CD,wBAAsB,sBAAsB,CAC3C,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC,EACnE,OAAO,GAAE,sBAA2B,GAClC,OAAO,CAAC,IAAI,CAAC,CAwCf"}
@@ -1,3 +1,6 @@
1
+ import {
2
+ detectViteProject
3
+ } from "./index-18hvb6gb.js";
1
4
  import {
2
5
  loadConfig,
3
6
  resolveConfigPath
@@ -13,6 +16,7 @@ async function runDoctorCommand(parsed, logger, options) {
13
16
  const cwd = options.cwd || process.cwd();
14
17
  const checks = [];
15
18
  const { fs } = await getDependencies();
19
+ const viteProject = await detectViteProject(cwd, fs);
16
20
  logger.info(`Running diagnostics...
17
21
  `);
18
22
  try {
@@ -67,63 +71,63 @@ async function runDoctorCommand(parsed, logger, options) {
67
71
  message: "devflare not in dependencies"
68
72
  });
69
73
  }
70
- if (deps.vite) {
74
+ } catch {
75
+ checks.push({
76
+ name: "package.json",
77
+ status: "fail",
78
+ message: "package.json not found"
79
+ });
80
+ }
81
+ if (viteProject.wantsViteIntegration) {
82
+ checks.push({
83
+ name: "Vite Integration",
84
+ status: "pass",
85
+ message: "Enabled for this package"
86
+ });
87
+ if (viteProject.hasLocalViteDependency) {
71
88
  checks.push({
72
89
  name: "vite dep",
73
90
  status: "pass",
74
- message: `Version: ${deps.vite}`
91
+ message: "Found in package.json"
75
92
  });
76
93
  } else {
77
94
  checks.push({
78
95
  name: "vite dep",
79
- status: "fail",
80
- message: "vite required but not found"
96
+ status: "warn",
97
+ message: "Not declared in this package.json (workspace-hoisted installs may still work)"
81
98
  });
82
99
  }
83
- if (deps["@cloudflare/vite-plugin"]) {
100
+ if (viteProject.hasLocalCloudflareVitePluginDependency) {
84
101
  checks.push({
85
102
  name: "@cloudflare/vite-plugin",
86
103
  status: "pass",
87
- message: `Version: ${deps["@cloudflare/vite-plugin"]}`
104
+ message: "Found in package.json"
88
105
  });
89
106
  } else {
90
107
  checks.push({
91
108
  name: "@cloudflare/vite-plugin",
92
- status: "fail",
93
- message: "@cloudflare/vite-plugin required but not found"
109
+ status: "warn",
110
+ message: "Not declared in this package.json"
94
111
  });
95
112
  }
96
- } catch {
97
- checks.push({
98
- name: "package.json",
99
- status: "fail",
100
- message: "package.json not found"
101
- });
102
- }
103
- const viteConfigPaths = [
104
- "vite.config.ts",
105
- "vite.config.js",
106
- "vite.config.mts",
107
- "vite.config.mjs"
108
- ];
109
- let viteConfigFound = false;
110
- for (const configName of viteConfigPaths) {
111
- try {
112
- await fs.access(resolve(cwd, configName));
113
- viteConfigFound = true;
113
+ if (viteProject.viteConfigPath) {
114
114
  checks.push({
115
115
  name: "Vite Config",
116
116
  status: "pass",
117
- message: `Found: ${configName}`
117
+ message: `Found: ${viteProject.viteConfigPath}`
118
118
  });
119
- break;
120
- } catch {}
121
- }
122
- if (!viteConfigFound) {
119
+ } else {
120
+ checks.push({
121
+ name: "Vite Config",
122
+ status: "warn",
123
+ message: "No vite.config found. Create one with @cloudflare/vite-plugin"
124
+ });
125
+ }
126
+ } else {
123
127
  checks.push({
124
- name: "Vite Config",
125
- status: "warn",
126
- message: "No vite.config found. Create one with @cloudflare/vite-plugin"
128
+ name: "Vite Integration",
129
+ status: "pass",
130
+ message: "Not enabled for this package (worker-only mode)"
127
131
  });
128
132
  }
129
133
  try {
@@ -0,0 +1,194 @@
1
+ import {
2
+ __require
3
+ } from "./index-37x76zdn.js";
4
+
5
+ // src/dev-server/vite-utils.ts
6
+ import { resolve } from "pathe";
7
+ import { spawn } from "node:child_process";
8
+ var VITE_CONFIG_FILES = [
9
+ "vite.config.ts",
10
+ "vite.config.js",
11
+ "vite.config.mts",
12
+ "vite.config.mjs"
13
+ ];
14
+ var ANSI_REGEX = /\x1b\[[0-9;]*m/g;
15
+ var LOCAL_VITE_URL_REGEX = /https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?\/?/i;
16
+ async function getNodeFs() {
17
+ return await import("node:fs/promises");
18
+ }
19
+ function safeParsePackageJson(content) {
20
+ try {
21
+ return JSON.parse(content);
22
+ } catch {
23
+ return {};
24
+ }
25
+ }
26
+ function readDependencyFlag(pkg, name) {
27
+ const dependencies = pkg.dependencies;
28
+ const devDependencies = pkg.devDependencies;
29
+ return Boolean(dependencies?.[name] ?? devDependencies?.[name]);
30
+ }
31
+ async function detectViteProject(cwd, fs) {
32
+ const fileSystem = fs ?? await getNodeFs();
33
+ let viteConfigPath = null;
34
+ for (const configName of VITE_CONFIG_FILES) {
35
+ const absolutePath = resolve(cwd, configName);
36
+ try {
37
+ await fileSystem.access(absolutePath);
38
+ viteConfigPath = absolutePath;
39
+ break;
40
+ } catch {
41
+ continue;
42
+ }
43
+ }
44
+ let pkg = {};
45
+ try {
46
+ const packageJson = await fileSystem.readFile(resolve(cwd, "package.json"), "utf-8");
47
+ pkg = safeParsePackageJson(packageJson);
48
+ } catch {
49
+ pkg = {};
50
+ }
51
+ const hasLocalViteDependency = readDependencyFlag(pkg, "vite");
52
+ const hasLocalCloudflareVitePluginDependency = readDependencyFlag(pkg, "@cloudflare/vite-plugin");
53
+ const wantsViteIntegration = Boolean(viteConfigPath || hasLocalViteDependency || hasLocalCloudflareVitePluginDependency);
54
+ return {
55
+ viteConfigPath,
56
+ hasLocalViteDependency,
57
+ hasLocalCloudflareVitePluginDependency,
58
+ shouldStartVite: Boolean(viteConfigPath),
59
+ wantsViteIntegration
60
+ };
61
+ }
62
+ function stripAnsi(value) {
63
+ return value.replace(ANSI_REGEX, "");
64
+ }
65
+ function extractViteReadyUrl(output) {
66
+ const cleaned = stripAnsi(output);
67
+ const lines = cleaned.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
68
+ for (const line of lines) {
69
+ if (!line.toLowerCase().includes("local:")) {
70
+ continue;
71
+ }
72
+ const match = line.match(LOCAL_VITE_URL_REGEX);
73
+ if (match) {
74
+ return match[0];
75
+ }
76
+ }
77
+ const fallbackMatch = cleaned.match(LOCAL_VITE_URL_REGEX);
78
+ return fallbackMatch?.[0] ?? null;
79
+ }
80
+ async function waitForViteReady(process, options = {}) {
81
+ const {
82
+ timeoutMs = 15000,
83
+ onStdout,
84
+ onStderr
85
+ } = options;
86
+ let combinedOutput = "";
87
+ return await new Promise((resolvePromise, rejectPromise) => {
88
+ let settled = false;
89
+ let timeout;
90
+ const settle = (resolver) => {
91
+ if (settled) {
92
+ return;
93
+ }
94
+ settled = true;
95
+ clearTimeout(timeout);
96
+ resolver();
97
+ };
98
+ const inspectChunk = (chunk) => {
99
+ combinedOutput += typeof chunk === "string" ? chunk : chunk.toString("utf-8");
100
+ const readyUrl = extractViteReadyUrl(combinedOutput);
101
+ if (readyUrl) {
102
+ settle(() => resolvePromise(readyUrl));
103
+ }
104
+ };
105
+ process.stdout?.on("data", (chunk) => {
106
+ onStdout?.(chunk);
107
+ inspectChunk(chunk);
108
+ });
109
+ process.stderr?.on("data", (chunk) => {
110
+ onStderr?.(chunk);
111
+ inspectChunk(chunk);
112
+ });
113
+ process.on("error", (error) => {
114
+ settle(() => rejectPromise(error));
115
+ });
116
+ process.on("exit", (code, signal) => {
117
+ settle(() => {
118
+ const reason = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
119
+ rejectPromise(new Error(`Vite exited before reporting a ready URL (${reason})`));
120
+ });
121
+ });
122
+ timeout = setTimeout(() => {
123
+ settle(() => resolvePromise(null));
124
+ }, timeoutMs);
125
+ });
126
+ }
127
+ async function defaultRunCommand(command, args) {
128
+ await new Promise((resolvePromise, rejectPromise) => {
129
+ const child = spawn(command, args, {
130
+ stdio: "ignore",
131
+ windowsHide: true
132
+ });
133
+ child.on("error", rejectPromise);
134
+ child.on("exit", () => resolvePromise());
135
+ });
136
+ }
137
+ function waitForProcessExit(process, timeoutMs) {
138
+ if (process.killed) {
139
+ return Promise.resolve(true);
140
+ }
141
+ return new Promise((resolvePromise) => {
142
+ let settled = false;
143
+ let timeout;
144
+ const settle = (value) => {
145
+ if (settled) {
146
+ return;
147
+ }
148
+ settled = true;
149
+ clearTimeout(timeout);
150
+ resolvePromise(value);
151
+ };
152
+ process.on("exit", () => settle(true));
153
+ timeout = setTimeout(() => {
154
+ settle(false);
155
+ }, timeoutMs);
156
+ });
157
+ }
158
+ async function stopSpawnedProcessTree(process, options = {}) {
159
+ const {
160
+ platform = globalThis.process?.platform ?? "linux",
161
+ timeoutMs = 3000,
162
+ runCommand = defaultRunCommand
163
+ } = options;
164
+ if (platform === "win32" && process.pid) {
165
+ try {
166
+ await runCommand("taskkill", ["/pid", String(process.pid), "/t", "/f"]);
167
+ } catch {
168
+ try {
169
+ process.kill("SIGTERM");
170
+ } catch {
171
+ return;
172
+ }
173
+ }
174
+ await waitForProcessExit(process, timeoutMs);
175
+ return;
176
+ }
177
+ try {
178
+ process.kill("SIGTERM");
179
+ } catch {
180
+ return;
181
+ }
182
+ const exited = await waitForProcessExit(process, timeoutMs);
183
+ if (exited) {
184
+ return;
185
+ }
186
+ try {
187
+ process.kill("SIGKILL");
188
+ } catch {
189
+ return;
190
+ }
191
+ await waitForProcessExit(process, timeoutMs);
192
+ }
193
+
194
+ export { detectViteProject, waitForViteReady, stopSpawnedProcessTree };
package/dist/index.js CHANGED
@@ -129,7 +129,7 @@ Usage:
129
129
 
130
130
  Commands:
131
131
  init [name] Create a new devflare project
132
- dev Start unified development server
132
+ dev Start development server
133
133
  build Build for production
134
134
  deploy Deploy to Cloudflare
135
135
  types Generate TypeScript types
@@ -145,23 +145,23 @@ Global Options:
145
145
  --debug Enable debug mode
146
146
 
147
147
  Dev Command Options:
148
- --port <port> Vite dev server port (default: 5173)
148
+ --port <port> Preferred Vite dev server port (default: 5173)
149
149
  --persist Persist Miniflare storage data
150
150
  --verbose Enable verbose logging
151
151
  --log Log all output to .log-{datetime} file AND terminal
152
152
  --log-temp Log all output to .log file (overwritten) AND terminal
153
153
 
154
154
  Dev Server Features:
155
- • Vite HMR for frontend (instant updates)
156
155
  • Miniflare for ALL Cloudflare bindings (KV, D1, R2, DOs, Queues, AI)
156
+ • Optional Vite HMR when a local vite.config.* is present
157
157
  • Rolldown for fast DO bundling with watch mode
158
158
  • DO hot reload when files change (no restart needed)
159
159
  • WebSocket bridge for seamless Node.js ↔ Miniflare communication
160
160
 
161
161
  Examples:
162
162
  devflare init my-app
163
- devflare dev # Start unified dev server
164
- devflare dev --port 3000 # Custom Vite port
163
+ devflare dev # Start worker-only or unified dev server
164
+ devflare dev --port 3000 # Custom Vite port when Vite is enabled
165
165
  devflare dev --persist # Persist storage between restarts
166
166
  devflare dev --log-temp # Log output to .log file
167
167
  devflare build
@@ -216,7 +216,7 @@ async function runInit(parsed, logger, options) {
216
216
  return runInitCommand(parsed, logger, options);
217
217
  }
218
218
  async function runDev(parsed, logger, options) {
219
- const { runDevCommand } = await import("./dev-qnxet3j9.js");
219
+ const { runDevCommand } = await import("./dev-pa8dhm20.js");
220
220
  return runDevCommand(parsed, logger, options);
221
221
  }
222
222
  async function runBuild(parsed, logger, options) {
@@ -232,7 +232,7 @@ async function runTypes(parsed, logger, options) {
232
232
  return runTypesCommand(parsed, logger, options);
233
233
  }
234
234
  async function runDoctor(parsed, logger, options) {
235
- const { runDoctorCommand } = await import("./doctor-e8fy6fj5.js");
235
+ const { runDoctorCommand } = await import("./doctor-fmgb3d28.js");
236
236
  return runDoctorCommand(parsed, logger, options);
237
237
  }
238
238
  async function runAccount(parsed, logger, options) {
@@ -30,7 +30,10 @@ export interface BridgeTestContext {
30
30
  *
31
31
  * @example
32
32
  * ```ts
33
- * import { createBridgeTestContext, bridgeEnv } from 'devflare'
33
+ * import { createBridgeTestContext } from 'devflare'
34
+ * import { createEnvProxy } from 'devflare'
35
+ *
36
+ * const bridgeEnv = createEnvProxy({ lazy: true })
34
37
  *
35
38
  * beforeAll(async () => {
36
39
  * await createBridgeTestContext({ configPath: './devflare.config.ts' })
@@ -1 +1 @@
1
- {"version":3,"file":"bridge-context.d.ts","sourceRoot":"","sources":["../../src/test/bridge-context.ts"],"names":[],"mappings":"AAMA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA;AAC3D,OAAO,EAA2D,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAG5I,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAMnD,MAAM,WAAW,wBAAwB;IACxC,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uDAAuD;IACvD,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACrC,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iCAAiC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sBAAsB;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC5B,6BAA6B;IAC7B,SAAS,EAAE,iBAAiB,CAAA;IAC5B,4BAA4B;IAC5B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,8CAA8C;IAC9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAYD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,uBAAuB,CAC5C,OAAO,GAAE,wBAA6B,GACpC,OAAO,CAAC,iBAAiB,CAAC,CAmF5B;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAI3D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,CAOxD;AAMD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAM1C,CAAA;AAqBF,OAAO,EAAE,cAAc,EAAE,CAAA"}
1
+ {"version":3,"file":"bridge-context.d.ts","sourceRoot":"","sources":["../../src/test/bridge-context.ts"],"names":[],"mappings":"AAMA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA;AAC3D,OAAO,EAA2D,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAG5I,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAMnD,MAAM,WAAW,wBAAwB;IACxC,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uDAAuD;IACvD,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACrC,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iCAAiC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sBAAsB;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC5B,6BAA6B;IAC7B,SAAS,EAAE,iBAAiB,CAAA;IAC5B,4BAA4B;IAC5B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,8CAA8C;IAC9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAYD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,uBAAuB,CAC5C,OAAO,GAAE,wBAA6B,GACpC,OAAO,CAAC,iBAAiB,CAAC,CAmF5B;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAI3D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,CAOxD;AAMD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAM1C,CAAA;AAqBF,OAAO,EAAE,cAAc,EAAE,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devflare",
3
- "version": "1.0.0-next.2",
3
+ "version": "1.0.0-next.3",
4
4
  "description": "Devflare is a developer-first toolkit for Cloudflare Workers that sits on top of Miniflare and Wrangler-compatible config",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",