import {
  INITIAL_STATE_WINDOW_PROPERTY,
  RUNTIME_CONFIG_WINDOW_PROPERTY,
  VERSION_CHECK_INTERVAL
} from "./chunk-6QRXRVYC.mjs";
import {
  API_VERSION,
  PAGE_COLUMN_COMPONENT_ID,
  PAGE_ROW_COMPONENT_ID,
  addFragment,
  addNode,
  applicationSchema,
  applyDiff,
  assertIsPage,
  compareFractionalIndex,
  createDom,
  createElement,
  createFragment,
  createId,
  createNode,
  createRenderTree,
  createSecret,
  getApp,
  getChildNodes,
  getMaybeNode,
  getNode,
  getPageByName,
  getQueryByName,
  getRequiredEnvVars,
  getRoot,
  isElement,
  isPage,
  isQuery,
  pageSchema,
  removeNode,
  setNodeNamespacedProp,
  themeSchema
} from "./chunk-ZUJ4UAKT.mjs";

// src/server/index.ts
import * as path8 from "path";
import { Worker, MessageChannel } from "worker_threads";
import express4 from "express";
import getPort from "get-port";
import { createProxyMiddleware } from "http-proxy-middleware";
import { mapValues as mapValues2 } from "@mui/toolpad-utils/collections";
import prettyBytes from "pretty-bytes";
import { WebSocket, WebSocketServer } from "ws";
import { listen } from "@mui/toolpad-utils/http";
import openBrowser from "react-dev-utils/openBrowser.js";
import chalk4 from "chalk";
import { serveRpc } from "@mui/toolpad-utils/workerRpc";
import * as url4 from "node:url";
import cors2 from "cors";

// src/utils/express.ts
function asyncHandler(handler) {
  return (req, res, next) => {
    Promise.resolve(handler(req, res, next)).catch(next);
  };
}

// src/server/toolpadAppServer.ts
import * as path from "path";
import * as fs from "fs/promises";
import * as express2 from "express";
import serializeJavascript from "serialize-javascript";

// src/server/config.ts
function readConfig() {
  if (typeof window !== "undefined") {
    throw new Error(`Serverside config can't be loaded on the client side`);
  }
  const encryptionKeys = process.env.TOOLPAD_ENCRYPTION_KEYS?.split(/\s+/).filter(Boolean) ?? [];
  let basicAuthConfig = {};
  if (process.env.TOOLPAD_BASIC_AUTH_USER && process.env.TOOLPAD_BASIC_AUTH_PASSWORD) {
    basicAuthConfig = {
      basicAuthUser: process.env.TOOLPAD_BASIC_AUTH_USER,
      basicAuthPassword: process.env.TOOLPAD_BASIC_AUTH_PASSWORD
    };
  } else if (process.env.TOOLPAD_BASIC_AUTH_USER) {
    throw new Error(
      `Basic Auth user configured without password. Please provide the TOOLPAD_BASIC_AUTH_PASSWORD environment variable.`
    );
  }
  return {
    ...basicAuthConfig,
    databaseUrl: process.env.TOOLPAD_DATABASE_URL,
    googleSheetsClientId: process.env.TOOLPAD_DATASOURCE_GOOGLESHEETS_CLIENT_ID,
    googleSheetsClientSecret: process.env.TOOLPAD_DATASOURCE_GOOGLESHEETS_CLIENT_SECRET,
    encryptionKeys
  };
}
var config_default = readConfig();

// src/server/basicAuth.ts
function checkBasicAuthHeader(headerContent) {
  if (!config_default.basicAuthUser) {
    return true;
  }
  if (headerContent) {
    const auth = headerContent.split(" ")[1];
    const [user, pwd] = atob(auth).toString().split(":");
    if (user === config_default.basicAuthUser && pwd === config_default.basicAuthPassword) {
      return true;
    }
  }
  return false;
}
function basicAuthUnauthorized(res) {
  res.setHeader("WWW-Authenticate", 'Basic realm="Protected"');
  res.statusCode = 401;
  res.end("Unauthorized");
}

// src/server/rpc.ts
import * as superjson from "superjson";
import express from "express";
import * as z from "zod";
import { fromZodError } from "zod-validation-error";
import { hasOwnProperty } from "@mui/toolpad-utils/collections";
import { errorFrom, serializeError } from "@mui/toolpad-utils/errors";
import { withContext, createServerContext } from "@mui/toolpad-core/serverRuntime";
var rpcRequestSchema = z.object({
  name: z.string(),
  params: z.array(z.any())
});
function createRpcHandler(definition) {
  const router = express.Router();
  router.post(
    "/",
    express.json(),
    asyncHandler(async (req, res) => {
      const parseResult = rpcRequestSchema.safeParse(req.body);
      if (!parseResult.success) {
        res.status(400).send(fromZodError(parseResult.error));
        return;
      }
      const { name, params } = parseResult.data;
      if (!hasOwnProperty(definition, name)) {
        res.status(404).end();
        return;
      }
      const method = definition[name];
      let rawResult;
      let error = null;
      try {
        const ctx = createServerContext(req, res);
        rawResult = await withContext(ctx, async () => {
          return method({ params, req, res });
        });
      } catch (rawError) {
        error = errorFrom(rawError);
      }
      const responseData = error ? { error: serializeError(error) } : { result: superjson.stringify(rawResult) };
      res.json(responseData);
    })
  );
  return router;
}
function createMethod(handler) {
  return handler;
}

// src/server/runtimeRpcServer.ts
function createRpcServer(project) {
  return {
    introspectDataProvider: createMethod(
      ({ params }) => {
        return project.functionsManager.introspectDataProvider(...params);
      }
    ),
    getDataProviderRecords: createMethod(
      ({ params }) => {
        return project.functionsManager.getDataProviderRecords(...params);
      }
    ),
    deleteDataProviderRecord: createMethod(({ params }) => {
      return project.functionsManager.deleteDataProviderRecord(...params);
    }),
    updateDataProviderRecord: createMethod(({ params }) => {
      return project.functionsManager.updateDataProviderRecord(...params);
    }),
    createDataProviderRecord: createMethod(({ params }) => {
      return project.functionsManager.createDataProviderRecord(...params);
    }),
    execQuery: createMethod(({ params }) => {
      return project.dataManager.execQuery(...params);
    }),
    execFunction: createMethod(({ params }) => {
      return project.functionsManager.execFunction(...params);
    })
  };
}

// src/runtime/createRuntimeState.tsx
function createRuntimeState({ dom }) {
  return {
    dom: createRenderTree(dom)
  };
}

// src/server/toolpadAppServer.ts
function postProcessHtml(html, { config: config2, initialState }) {
  const serializedConfig = serializeJavascript(config2, { ignoreFunction: true });
  const serializedInitialState = serializeJavascript(initialState, { isJSON: true });
  const toolpadScripts = [
    `<script>window[${JSON.stringify(
      RUNTIME_CONFIG_WINDOW_PROPERTY
    )}] = ${serializedConfig}</script>`,
    `<script>window[${JSON.stringify(
      INITIAL_STATE_WINDOW_PROPERTY
    )}] = ${serializedInitialState}</script>`
  ];
  return html.replace(`<!-- __TOOLPAD_SCRIPTS__ -->`, () => toolpadScripts.join("\n"));
}
async function createProdHandler(project) {
  const handler = express2.Router();
  handler.use(express2.static(project.getAppOutputFolder(), { index: false }));
  handler.use((req, res, next) => {
    if (checkBasicAuthHeader(req.headers.authorization ?? null)) {
      next();
      return;
    }
    basicAuthUnauthorized(res);
  });
  handler.use("/api/data", project.dataManager.createDataHandler());
  const runtimeRpcServer = createRpcServer(project);
  handler.use("/api/runtime-rpc", createRpcHandler(runtimeRpcServer));
  handler.use(
    asyncHandler(async (req, res) => {
      const htmlFilePath = path.resolve(project.getAppOutputFolder(), "./index.html");
      const [runtimeConfig, dom] = await Promise.all([
        project.getRuntimeConfig(),
        project.loadDom()
      ]);
      let html = await fs.readFile(htmlFilePath, { encoding: "utf-8" });
      html = postProcessHtml(html, {
        config: runtimeConfig,
        initialState: createRuntimeState({ dom })
      });
      res.setHeader("Content-Type", "text/html; charset=utf-8").status(200).end(html);
    })
  );
  return { handler, dispose: async () => void 0 };
}

// src/server/localMode.ts
import * as path7 from "path";
import * as fs5 from "fs/promises";
import { isMainThread } from "worker_threads";
import * as yaml from "yaml";
import invariant3 from "invariant";
import openEditor from "open-editor";
import chalk3 from "chalk";
import { fromZodError as fromZodError3 } from "zod-validation-error";
import { glob as glob2 } from "glob";
import * as chokidar3 from "chokidar";
import { debounce, throttle } from "lodash-es";
import { Emitter } from "@mui/toolpad-utils/events";
import { guessTitle } from "@mui/toolpad-utils/strings";
import { errorFrom as errorFrom8 } from "@mui/toolpad-utils/errors";
import { filterValues, hasOwnProperty as hasOwnProperty2, mapValues } from "@mui/toolpad-utils/collections";
import { execa } from "execa";
import {
  writeFileRecursive as writeFileRecursive2,
  readMaybeFile,
  readMaybeDir,
  updateYamlFile,
  fileExists as fileExists3,
  readJsonFile as readJsonFile2
} from "@mui/toolpad-utils/fs";
import { z as z3 } from "zod";

// src/utils/insecureHash.ts
function insecureHash(str) {
  let h = 0;
  for (let i = 0; i < str.length; i += 1) {
    h = Math.imul(31, h) + str.charCodeAt(i) | 0;
  }
  return h;
}

// src/utils/prettier.ts
import prettier from "prettier";
import parserBabel from "prettier/parser-babel.js";
var DEFAULT_OPTIONS = {
  parser: "babel-ts",
  plugins: [parserBabel]
};
async function resolvePrettierConfig(filePath) {
  const config2 = await prettier.resolveConfig(filePath);
  return config2;
}
async function format(code, filePath) {
  const readConfig2 = await resolvePrettierConfig(filePath);
  return prettier.format(code, {
    ...readConfig2,
    ...DEFAULT_OPTIONS
  });
}

// src/server/EnvManager.ts
import * as path2 from "path";
import * as chokidar from "chokidar";
import * as dotenv from "dotenv";
import chalk from "chalk";
import { truncate } from "@mui/toolpad-utils/strings";
function getEnvFilePath() {
  return path2.resolve(process.cwd(), ".env");
}
var EnvManager = class {
  project;
  originalEnv = { ...process.env };
  values = {};
  watcher;
  constructor(project) {
    this.project = project;
  }
  async start() {
    this.loadEnvFile();
    if (this.project.options.dev) {
      this.initWatcher();
    }
  }
  // eslint-disable-next-line class-methods-use-this
  async build() {
  }
  async dispose() {
    await this.watcher?.close();
  }
  resetEnv() {
    Object.keys(process.env).forEach((key) => {
      delete process.env[key];
    });
    Object.assign(process.env, this.originalEnv);
  }
  loadEnvFile() {
    const envFilePath = getEnvFilePath();
    this.resetEnv();
    const { parsed = {} } = dotenv.config({ path: envFilePath, override: true });
    this.values = parsed;
    console.log(
      `${chalk.blue("info")}  - loaded env file "${envFilePath}" with keys ${truncate(
        Object.keys(parsed).join(", "),
        1e3
      )}`
    );
  }
  initWatcher() {
    if (!this.project.options.dev) {
      return;
    }
    this.watcher = chokidar.watch([getEnvFilePath()], {
      usePolling: true,
      ignoreInitial: true
    });
    const handleChange = async () => {
      this.loadEnvFile();
      this.project.events.emit("envChanged", {});
      this.project.invalidateQueries();
    };
    this.watcher.on("add", handleChange);
    this.watcher.on("unlink", handleChange);
    this.watcher.on("change", handleChange);
  }
  async getDeclaredValues() {
    return this.values;
  }
  // eslint-disable-next-line class-methods-use-this
  getEnv() {
    return process.env;
  }
};

// src/server/FunctionsManager.ts
import * as path4 from "path";
import * as fs3 from "fs/promises";
import * as esbuild from "esbuild";
import { ensureSuffix, indent } from "@mui/toolpad-utils/strings";
import * as chokidar2 from "chokidar";
import chalk2 from "chalk";
import { glob } from "glob";
import { writeFileRecursive, fileExists, readJsonFile } from "@mui/toolpad-utils/fs";
import Piscina from "piscina";
import { errorFrom as errorFrom2 } from "@mui/toolpad-utils/errors";
import * as url2 from "node:url";
import invariant from "invariant";

// src/server/functionsRuntime.ts
import * as path3 from "path";
import { createRequire } from "node:module";
import * as fs2 from "fs/promises";
import * as vm from "vm";
import * as url from "node:url";
import { initialContextStore } from "@mui/toolpad-core/serverRuntime";
import { TOOLPAD_DATA_PROVIDER_MARKER } from "@mui/toolpad-core/server";
import * as z2 from "zod";
import { fromZodError as fromZodError2 } from "zod-validation-error";
import.meta.url ??= url.pathToFileURL(__filename).toString();
var fileContents = /* @__PURE__ */ new Map();
var moduleCache = /* @__PURE__ */ new Map();
function loadModule(fullPath, content) {
  const moduleRequire = createRequire(url.pathToFileURL(fullPath));
  const moduleObject = { exports: {} };
  const serverRuntime = moduleRequire("@mui/toolpad/server");
  serverRuntime.__initContextStore(initialContextStore);
  vm.runInThisContext(`((require, exports, module) => {
${content}
})`)(
    moduleRequire,
    moduleObject.exports,
    moduleObject
  );
  return moduleObject;
}
async function loadExports(filePath) {
  const fullPath = path3.resolve(filePath);
  const content = await fs2.readFile(fullPath, "utf-8");
  if (content !== fileContents.get(fullPath)) {
    moduleCache.delete(fullPath);
    fileContents.set(fullPath, content);
  }
  let cachedModule = moduleCache.get(fullPath);
  if (!cachedModule) {
    cachedModule = loadModule(fullPath, content);
    moduleCache.set(fullPath, cachedModule);
  }
  return new Map(Object.entries(cachedModule.exports));
}
var dataProviderSchema = z2.object({
  paginationMode: z2.enum(["index", "cursor"]).optional().default("index"),
  getRecords: z2.function(z2.tuple([z2.any()]), z2.any()),
  deleteRecord: z2.function(z2.tuple([z2.any()]), z2.any()).optional(),
  updateRecord: z2.function(z2.tuple([z2.any(), z2.any()]), z2.any()).optional(),
  createRecord: z2.function(z2.tuple([z2.any()]), z2.any()).optional(),
  [TOOLPAD_DATA_PROVIDER_MARKER]: z2.literal(true)
});
async function loadDataProvider(filePath, name) {
  const exports = await loadExports(filePath);
  const dataProviderExport = exports.get(name);
  if (!dataProviderExport || typeof dataProviderExport !== "object") {
    throw new Error(`DataProvider "${name}" not found`);
  }
  const parsed = dataProviderSchema.safeParse(dataProviderExport);
  if (parsed.success) {
    return parsed.data;
  }
  throw fromZodError2(parsed.error);
}
async function execute(filePath, name, parameters) {
  const exports = await loadExports(filePath);
  const fn = exports.get(name);
  if (typeof fn !== "function") {
    throw new Error(`Function "${name}" not found`);
  }
  const result = await fn(...parameters);
  return result;
}

// src/server/functionsShared.ts
import ts from "typescript";
var compilerOptions = {
  noEmit: true,
  target: ts.ScriptTarget.ESNext,
  lib: ["lib.esnext.d.ts"],
  types: ["node"],
  // NOTE: strictNullChecks is essential for the type extraction to work properly. When we decide
  // to support user-defined tsconfig.json, we must make sure this option is enabled.
  strictNullChecks: true,
  module: ts.ModuleKind.CommonJS,
  moduleResolution: ts.ModuleResolutionKind.Bundler,
  esModuleInterop: true,
  allowSyntheticDefaultImports: true
};

// src/server/FunctionsManager.ts
import.meta.url ??= url2.pathToFileURL(__filename).toString();
var currentDirectory = url2.fileURLToPath(new URL(".", import.meta.url));
async function createDefaultFunction(filePath) {
  const result = await format(
    `
    /**
     * Toolpad handlers file.
     */

    export default async function handler (message: string) {
      return \`Hello \${message}\`;
    }
  `,
    filePath
  );
  return result;
}
async function createDefaultDataProvider(filePath, options) {
  const result = await format(
    `
    /**
     * Toolpad data provider file.
     * See: https://mui.com/toolpad/concepts/data-providers/
     */

    import { createDataProvider } from '@mui/toolpad/server';

    export default createDataProvider({
      ${options.paginationMode === "cursor" ? 'paginationMode: "cursor",' : ""}
      async getRecords({ paginationModel: ${options.paginationMode === "cursor" ? "{ cursor, pageSize }" : "{ start, pageSize }"} }) {
        return {
          records: [],
          ${options.paginationMode === "cursor" ? "cursor: null," : ""}
        };
      }
    })
  `,
    filePath
  );
  return result;
}
function formatCodeFrame(location) {
  const lineNumberCharacters = Math.ceil(Math.log10(location.line));
  return [
    `${location.file}:${location.line}:${location.column}:`,
    `  ${location.line} \u2502 ${location.lineText}`,
    `  ${" ".repeat(lineNumberCharacters)} \u2575 ${" ".repeat(
      Math.max(location.lineText.length - 1, 0)
    )}^`
  ].join("\n");
}
function formatError(esbuildError) {
  let messageText = esbuildError.text;
  if (esbuildError.location) {
    const formattedLocation = indent(formatCodeFrame(esbuildError.location), 2);
    messageText = [messageText, formattedLocation].join("\n");
  }
  return new Error(messageText);
}
var FunctionsManager = class {
  project;
  buildErrors = [];
  extractedTypes;
  extractTypesWorker;
  buildCtx;
  constructor(project) {
    this.project = project;
  }
  getResourcesFolder() {
    return path4.join(this.project.getRoot(), "./resources");
  }
  getFunctionsFile() {
    return path4.join(this.getResourcesFolder(), "./functions.ts");
  }
  async getFunctionFilePath(fileName) {
    const resourcesFolder = this.getResourcesFolder();
    return path4.join(resourcesFolder, fileName);
  }
  getFunctionResourcesPattern() {
    return path4.join(this.getResourcesFolder(), "*.ts");
  }
  async migrateLegacy() {
    const legacyQueriesFile = path4.resolve(this.project.getRoot(), "queries.ts");
    if (await fileExists(legacyQueriesFile)) {
      const functionsFile = this.getFunctionsFile();
      await fs3.mkdir(path4.dirname(functionsFile), { recursive: true });
      await fs3.rename(legacyQueriesFile, functionsFile);
    }
  }
  async getFunctionFiles() {
    const paths = await glob(this.getFunctionResourcesPattern(), { windowsPathsNoEscape: true });
    return paths.map((fullPath) => path4.relative(this.project.getRoot(), fullPath));
  }
  getBuildErrorsForFile(entryPoint) {
    return this.buildErrors.filter((error) => error.location?.file === entryPoint);
  }
  getFunctionsOutputFolder() {
    return path4.resolve(this.project.getOutputFolder(), "functions");
  }
  getIntrospectJsonPath() {
    return path4.resolve(this.getFunctionsOutputFolder(), "introspect.json");
  }
  async extractTypes() {
    if (!this.extractTypesWorker) {
      this.extractTypesWorker = new Piscina({
        filename: path4.resolve(currentDirectory, "../cli/functionsTypesWorker.mjs")
      });
    }
    const extractedTypes = this.extractTypesWorker.run({ resourcesFolder: this.getResourcesFolder() }, {}).catch((error) => ({
      error,
      files: []
    }));
    return extractedTypes;
  }
  async createEsbuildContext() {
    const root = this.project.getRoot();
    const onFunctionBuildStart = async () => {
      this.extractedTypes = void 0;
    };
    const onFunctionsBuildEnd = async (args) => {
      console.log(
        `${chalk2.green("ready")} - built functions.ts: ${args.errors.length} error(s), ${args.warnings.length} warning(s)`
      );
      this.buildErrors = args.errors;
      this.project.invalidateQueries();
      this.project.events.emit("functionsChanged", {});
    };
    const toolpadPlugin = {
      name: "toolpad",
      setup(build) {
        build.onStart(onFunctionBuildStart);
        build.onEnd(onFunctionsBuildEnd);
      }
    };
    const entryPoints = await this.getFunctionFiles();
    return esbuild.context({
      absWorkingDir: root,
      entryPoints,
      plugins: [toolpadPlugin],
      write: true,
      bundle: true,
      metafile: true,
      outdir: this.getFunctionsOutputFolder(),
      platform: "node",
      packages: "external",
      target: "es2022",
      tsconfigRaw: JSON.stringify({ compilerOptions }),
      loader: {
        ".txt": "text",
        ".sql": "text"
      }
    });
  }
  async startWatchingFunctionFiles() {
    const resourcesWatcher = chokidar2.watch([this.getFunctionResourcesPattern()], {
      ignoreInitial: true
    });
    const reinitializeWatcher = async () => {
      await this.buildCtx?.dispose();
      this.buildCtx = await this.createEsbuildContext();
      await this.buildCtx.watch();
    };
    reinitializeWatcher();
    resourcesWatcher.on("add", reinitializeWatcher);
    resourcesWatcher.on("unlink", reinitializeWatcher);
  }
  async start() {
    if (this.project.options.dev) {
      await this.migrateLegacy();
      await this.startWatchingFunctionFiles();
    }
  }
  async build() {
    const ctx = await this.createEsbuildContext();
    await ctx.rebuild();
    await ctx.dispose();
    const types = await this.extractTypes();
    if (types.error) {
      throw errorFrom2(types.error);
    }
    await fs3.mkdir(this.getFunctionsOutputFolder(), { recursive: true });
    await fs3.writeFile(this.getIntrospectJsonPath(), JSON.stringify(types, null, 2), "utf-8");
  }
  async disposeBuildcontext() {
    this.buildCtx?.dispose();
    this.buildCtx = void 0;
  }
  async dispose() {
    await Promise.all([this.disposeBuildcontext(), this.extractTypesWorker?.destroy()]);
  }
  async getBuiltOutputFilePath(fileName) {
    const resourcesFolder = this.getResourcesFolder();
    const fullPath = path4.resolve(resourcesFolder, fileName);
    const entryPoint = path4.relative(this.project.getRoot(), fullPath);
    const buildErrors = this.getBuildErrorsForFile(entryPoint);
    if (buildErrors.length > 0) {
      throw formatError(buildErrors[0]);
    }
    const outputFilePath = path4.resolve(
      this.getFunctionsOutputFolder(),
      `${path4.basename(fileName, ".ts")}.js`
    );
    return outputFilePath;
  }
  async exec(fileName, name, parameters) {
    const extractedTypes = await this.introspect();
    if (extractedTypes.error) {
      throw errorFrom2(extractedTypes.error);
    }
    const file = extractedTypes.files.find((fileEntry) => fileEntry.name === fileName);
    const handler = file?.handlers.find((handlerEntry) => handlerEntry.name === name);
    if (!handler) {
      throw new Error(`No function found with the name "${name}"`);
    }
    const executeParams = handler.isCreateFunction ? [{ parameters }] : handler.parameters.map(([parameterName]) => parameters[parameterName]);
    return this.execFunction(fileName, name, executeParams);
  }
  async execFunction(fileName, name, parameters) {
    const outputFilePath = await this.getBuiltOutputFilePath(fileName);
    const data = await execute(outputFilePath, name, parameters);
    return { data };
  }
  async introspect() {
    if (!this.extractedTypes) {
      if (this.project.options.dev) {
        this.extractedTypes = this.extractTypes();
      } else {
        this.extractedTypes = readJsonFile(
          this.getIntrospectJsonPath()
        );
      }
    }
    return this.extractedTypes;
  }
  async createFunctionFile(name) {
    const filePath = path4.resolve(this.getResourcesFolder(), ensureSuffix(name, ".ts"));
    const content = await createDefaultFunction(filePath);
    if (await fileExists(filePath)) {
      throw new Error(`"${name}" already exists`);
    }
    await writeFileRecursive(filePath, content, { encoding: "utf-8" });
    this.extractedTypes = void 0;
  }
  async createDataProviderFile(name, options) {
    const filePath = path4.resolve(this.getResourcesFolder(), ensureSuffix(name, ".ts"));
    const content = await createDefaultDataProvider(filePath, options);
    if (await fileExists(filePath)) {
      throw new Error(`"${name}" already exists`);
    }
    await writeFileRecursive(filePath, content, { encoding: "utf-8" });
    this.extractedTypes = void 0;
  }
  async introspectDataProvider(fileName, exportName = "default") {
    const fullPath = await this.getBuiltOutputFilePath(fileName);
    const dataProvider = await loadDataProvider(fullPath, exportName);
    return {
      paginationMode: dataProvider.paginationMode,
      hasDeleteRecord: !!dataProvider.deleteRecord,
      hasUpdateRecord: !!dataProvider.updateRecord,
      hasCreateRecord: !!dataProvider.createRecord
    };
  }
  async getDataProviderRecords(fileName, exportName, params) {
    const fullPath = await this.getBuiltOutputFilePath(fileName);
    const dataProvider = await loadDataProvider(fullPath, exportName);
    return dataProvider.getRecords(params);
  }
  async deleteDataProviderRecord(fileName, exportName, id) {
    const fullPath = await this.getBuiltOutputFilePath(fileName);
    const dataProvider = await loadDataProvider(fullPath, exportName);
    invariant(dataProvider.deleteRecord, "DataProvider does not support deleteRecord");
    return dataProvider.deleteRecord(id);
  }
  async updateDataProviderRecord(fileName, exportName, id, values) {
    const fullPath = await this.getBuiltOutputFilePath(fileName);
    const dataProvider = await loadDataProvider(fullPath, exportName);
    invariant(dataProvider.updateRecord, "DataProvider does not support updateRecord");
    return dataProvider.updateRecord(id, values);
  }
  async createDataProviderRecord(fileName, exportName, values) {
    const fullPath = await this.getBuiltOutputFilePath(fileName);
    const dataProvider = await loadDataProvider(fullPath, exportName);
    invariant(dataProvider.createRecord, "DataProvider does not support createRecord");
    return dataProvider.createRecord(values);
  }
};

// src/server/versionInfo.ts
import * as path5 from "path";
import latestVersion from "latest-version";
import * as semver from "semver";
import { fileExists as fileExists2 } from "@mui/toolpad-utils/fs";

// package.json
var package_default = {
  name: "@mui/toolpad",
  version: "0.1.42",
  license: "MIT",
  bin: {
    toolpad: "./cli.mjs"
  },
  scripts: {
    build: 'concurrently "yarn:build:*"',
    dev: 'concurrently "yarn:dev:*"',
    cli: "./cli.mjs",
    lint: "prettier --check .",
    fix: "prettier --write .",
    "build:cli": "tsup",
    "build:editor": "vite build ./src/toolpad",
    "build:typings": "tsx ./scripts/typings.mts",
    "dev:cli": "tsup --watch",
    "dev:typings": "yarn build:typings",
    waitForApp: "tsx ./scripts/waitForApp.mts",
    "check-types": "tsup && tsc --noEmit",
    test: "vitest run"
  },
  files: [
    "public",
    "scripts",
    "dist",
    "cli.mjs",
    "browser",
    "server",
    "runtime"
  ],
  exports: {
    "./package.json": "./package.json",
    "./cli": {
      require: "./dist/cli/index.js"
    },
    ".": {
      types: "./dist/exports/index.d.ts",
      import: "./dist/exports/index.mjs",
      require: "./dist/exports/index.js"
    },
    "./*": {
      types: "./dist/exports/*.d.ts",
      import: "./dist/exports/*.mjs",
      require: "./dist/exports/*.js"
    }
  },
  dependencies: {
    "@emotion/cache": "11.11.0",
    "@emotion/react": "11.11.1",
    "@emotion/server": "11.11.0",
    "@emotion/styled": "11.11.0",
    "@googleapis/drive": "8.4.0",
    "@googleapis/sheets": "5.0.5",
    "@mui/icons-material": "5.15.1",
    "@mui/lab": "5.0.0-alpha.157",
    "@mui/material": "5.15.1",
    "@mui/system": "5.15.1",
    "@mui/toolpad-components": "0.1.42",
    "@mui/toolpad-core": "0.1.42",
    "@mui/toolpad-utils": "0.1.42",
    "@mui/types": "7.2.11",
    "@mui/utils": "5.15.1",
    "@mui/x-charts": "6.18.3",
    "@mui/x-data-grid": "6.18.5",
    "@mui/x-data-grid-pro": "6.18.5",
    "@mui/x-date-pickers": "6.18.5",
    "@mui/x-date-pickers-pro": "6.18.5",
    "@mui/x-tree-view": "6.17.0",
    "@tanstack/react-query": "5.14.1",
    "@tanstack/react-query-devtools": "5.14.1",
    "@types/cors": "2.8.17",
    "@types/json-schema": "7.0.15",
    "@types/node": "20.10.5",
    "@types/react-dev-utils": "9.0.15",
    "@vitejs/plugin-react": "4.2.1",
    "@webcontainer/env": "1.1.0",
    "abort-controller": "3.0.0",
    arg: "5.0.2",
    "basic-auth": "2.0.1",
    chalk: "5.3.0",
    chokidar: "3.5.3",
    clsx: "2.0.0",
    concurrently: "8.2.2",
    cors: "2.8.5",
    csstype: "3.1.3",
    dayjs: "1.11.10",
    dotenv: "16.3.1",
    esbuild: "0.19.9",
    execa: "8.0.1",
    express: "4.18.2",
    "find-up": "7.0.0",
    "fractional-indexing": "3.2.0",
    "get-port": "7.0.0",
    glob: "10.3.10",
    "google-auth-library": "9.4.1",
    "http-proxy-middleware": "2.0.6",
    invariant: "2.2.4",
    "json-to-ts": "1.7.0",
    json5: "2.2.3",
    "latest-version": "7.0.0",
    "lodash-es": "4.17.21",
    "markdown-to-jsx": "7.3.2",
    mime: "4.0.1",
    mysql2: "3.6.5",
    nanoid: "5.0.4",
    "node-fetch": "2.7.0",
    "node-fetch-har": "1.0.1",
    "open-editor": "4.1.1",
    "path-to-regexp": "6.2.1",
    "perf-cascade": "3.0.3",
    pg: "8.11.3",
    piscina: "4.2.1",
    prettier: "2.8.8",
    "pretty-bytes": "6.1.1",
    react: "18.2.0",
    "react-dev-utils": "12.0.1",
    "react-dom": "18.2.0",
    "react-error-boundary": "4.0.12",
    "react-hook-form": "7.49.2",
    "react-inspector": "6.0.2",
    "react-is": "18.2.0",
    "react-resizable-panels": "1.0.3",
    "react-router-dom": "6.21.0",
    recharts: "2.10.3",
    semver: "7.5.4",
    "serialize-javascript": "6.0.1",
    superjson: "2.0.0",
    typescript: "5.3.3",
    vite: "5.0.10",
    "vm-browserify": "1.1.2",
    "whatwg-url": "14.0.0",
    ws: "8.15.1",
    yaml: "2.3.4",
    "yaml-diff-patch": "2.0.0",
    yargs: "17.7.2",
    zod: "3.22.4",
    "zod-validation-error": "2.1.0"
  },
  devDependencies: {
    "@types/babel__code-frame": "7.0.6",
    "@types/express": "4.17.21",
    "@types/express-serve-static-core": "4.17.41",
    "@types/formidable": "3.4.5",
    "@types/glob": "8.1.0",
    "@types/har-format": "1.2.15",
    "@types/invariant": "2.2.37",
    "@types/lodash-es": "4.17.12",
    "@types/node-fetch": "2.6.9",
    "@types/pg": "8.10.9",
    "@types/prettier": "2.7.3",
    "@types/react": "18.2.45",
    "@types/react-dom": "18.2.18",
    "@types/react-is": "18.2.4",
    "@types/semver": "7.5.6",
    "@types/serialize-javascript": "5.0.4",
    "@types/whatwg-url": "11.0.3",
    "@types/ws": "8.5.10",
    "@types/yargs": "17.0.32",
    ajv: "8.12.0",
    eslint: "8.56.0",
    "eslint-config-prettier": "9.1.0",
    "eslint-plugin-import": "2.29.1",
    formidable: "3.5.1",
    "monaco-editor": "0.45.0",
    "react-devtools-inline": "5.0.0",
    "react-transition-group": "4.4.5",
    webpack: "5.89.0"
  },
  typings: "./index.d.ts",
  publishConfig: {
    access: "public"
  }
};

// src/server/versionInfo.ts
async function detectPackageManager(root) {
  const [hasYarnLock, hasPackageLock, hasPnpmLock] = await Promise.all([
    fileExists2(path5.resolve(root, "./yarn.lock")),
    fileExists2(path5.resolve(root, "./package-lock.lock")),
    fileExists2(path5.resolve(root, "./pnpm-lock.lock"))
  ]);
  if (hasYarnLock) {
    return "yarn";
  }
  if (hasPnpmLock) {
    return "pnpm";
  }
  if (hasPackageLock) {
    return "npm";
  }
  return null;
}
async function checkVersion(root) {
  const pkgName = "@mui/toolpad";
  const [latest, packageManager] = await Promise.all([
    latestVersion(pkgName),
    detectPackageManager(root)
  ]);
  const current = package_default.version;
  const updateAvailable = semver.compare(latest, current) > 0;
  return { current, latest, updateAvailable, packageManager };
}

// src/server/DataManager.ts
import { withContext as withContext2, createServerContext as createServerContext2 } from "@mui/toolpad-core/serverRuntime";
import { createServerJsRuntime as createServerJsRuntime2 } from "@mui/toolpad-core/jsServerRuntime";
import express3 from "express";
import cors from "cors";
import invariant2 from "invariant";
import { errorFrom as errorFrom7, serializeError as serializeError4 } from "@mui/toolpad-utils/errors";

// src/toolpadDataSources/postgres/server.ts
import pg from "pg";
import { errorFrom as errorFrom4 } from "@mui/toolpad-utils/errors";

// src/toolpadDataSources/sql/server.ts
import { serializeError as serializeError2, errorFrom as errorFrom3 } from "@mui/toolpad-utils/errors";
function createSqlServerDatasource({
  execSql: execSql3,
  testConnection: testConnection3
}) {
  return {
    exec: async (connection, query, params) => {
      const { data, error } = await execSql3(connection, query, params);
      return { data, error };
    },
    execPrivate: async (connection, query) => {
      switch (query.kind) {
        case "debugExec":
          return execSql3(connection, query.query, query.params);
        case "connectionStatus": {
          let connectionStatus;
          try {
            await testConnection3(query.params);
            connectionStatus = { status: "success" };
            return { data: connectionStatus };
          } catch (rawError) {
            const serializedError = serializeError2(errorFrom3(rawError));
            connectionStatus = {
              status: "error",
              error: serializedError.message
            };
            return { data: connectionStatus, error: serializedError };
          }
        }
        default:
          throw new Error(`Unknown query "${query.kind}"`);
      }
    },
    api: {}
  };
}

// src/toolpadDataSources/postgres/server.ts
var { Client } = pg;
function parseQuery(sql, params) {
  const substitutions = new Map(params.map(([name], i) => [name.toLowerCase(), i + 1]));
  const sqlWithNamedVars = sql.replaceAll(/\$([a-zA-Z][a-zA-Z0-9]*)/g, (match2, varName) => {
    const index = substitutions.get(varName.toLowerCase());
    if (typeof index === "number") {
      return `$${index}`;
    }
    return match2;
  });
  return {
    text: sqlWithNamedVars,
    values: params.map(([, value]) => value)
  };
}
function parseErrorMessage(msg, params) {
  const substitutions = new Map(params.map(([name], i) => [i + 1, name]));
  const msgWithNamedVars = msg.replaceAll(/\$(\d+)/g, (match2, index) => {
    const varName = substitutions.get(Number(index));
    if (typeof varName === "string") {
      return `$${varName}(${match2})`;
    }
    return match2;
  });
  return msgWithNamedVars;
}
async function execSql(connection, postgresQuery, params) {
  if (!connection?.password) {
    throw new Error(`Password required`);
  }
  const client = new Client({ ...connection });
  const paramEntries = Object.entries(params);
  try {
    await client.connect();
    const pgQuery = parseQuery(postgresQuery.sql, paramEntries);
    const res = await client.query(pgQuery);
    return {
      data: res.rows,
      info: res.command !== "SELECT" ? `OK ${res.command}, ${res.rowCount} ${res.rowCount === 1 ? "row" : "rows"} affected` : void 0
    };
  } catch (rawError) {
    const error = errorFrom4(rawError);
    error.message = parseErrorMessage(error.message, paramEntries);
    throw error;
  } finally {
    await client.end();
  }
}
var testConnection = async (connection) => {
  const client = new Client({ ...connection });
  await client.connect();
  await client.query("SELECT * FROM version();");
};
var server_default = createSqlServerDatasource({
  execSql,
  testConnection
});

// src/toolpadDataSources/mysql/server.ts
import { createConnection } from "mysql2/promise";
import { errorFrom as errorFrom5 } from "@mui/toolpad-utils/errors";
function prepareQuery(sql, params) {
  const substitutions = [];
  const sqlWithVarsReplaced = sql.replaceAll(
    // eslint-disable-next-line no-useless-escape
    /(?<==|<|<=|>|>=|<>)[ ]*(([\$\w\d]*\b)|([']+[\w ]+['][ ]*))|(;)/g,
    (match2) => {
      const trimmedMatch = match2.trim().replaceAll(/[']+/g, "");
      if (trimmedMatch[0] === "$") {
        const varName = trimmedMatch.slice(1);
        if (typeof params[varName] !== "undefined") {
          substitutions.push(params[varName]);
        }
        return "?";
      }
      if (trimmedMatch === ";") {
        return "";
      }
      substitutions.push(trimmedMatch);
      return "?";
    }
  );
  return {
    sql: sqlWithVarsReplaced,
    substitutions
  };
}
async function execSql2(connection, query, params) {
  const mysqlConnection = await createConnection({ ...connection });
  try {
    const { sql, substitutions } = prepareQuery(query.sql, params);
    const [result] = await mysqlConnection.execute(sql, substitutions);
    let rows = [];
    let info;
    if (Array.isArray(result)) {
      rows = result;
    } else {
      info = result.info || result.message;
    }
    return {
      data: rows,
      info
    };
  } catch (rawError) {
    const error = errorFrom5(rawError);
    throw error;
  } finally {
    await mysqlConnection.end();
  }
}
async function testConnection2(connection) {
  const mysqlConnection = await createConnection({ ...connection });
  mysqlConnection.execute("SELECT 1");
}
var server_default2 = createSqlServerDatasource({
  execSql: execSql2,
  testConnection: testConnection2
});

// src/toolpadDataSources/rest/server.ts
import fetch3, { Headers } from "node-fetch";
import { createServerJsRuntime } from "@mui/toolpad-core/jsServerRuntime";
import { errorFrom as errorFrom6, serializeError as serializeError3 } from "@mui/toolpad-utils/errors";
import { evaluateBindable } from "@mui/toolpad-core/jsRuntime";
import { removePrefix } from "@mui/toolpad-utils/strings";

// src/server/har.ts
import { withHar as withHarOriginal } from "node-fetch-har";
import fetch2, { Request } from "node-fetch";

// src/utils/har.ts
function createHarLog() {
  return {
    log: {
      version: "0.0",
      creator: {
        name: "Toolpad",
        version: process.env.TOOLPAD_VERSION || ""
      },
      entries: []
    }
  };
}

// src/server/har.ts
var withHarInstrumentation = function withHar(fetchFn, options) {
  const withHarFetch = withHarOriginal(fetchFn, options);
  const patchedFetch = (...args) => {
    const req = new Request(...args);
    const input = req.url;
    return withHarFetch(input, {
      agent: req.agent,
      body: req.body,
      compress: req.compress,
      follow: req.follow,
      headers: req.headers,
      method: req.method,
      redirect: req.redirect
    });
  };
  patchedFetch.isRedirect = fetch2.isRedirect;
  return patchedFetch;
};

// src/toolpadDataSources/applyTransform.ts
async function applyTransform(jsRuntime, transform, data) {
  const transformFn = `(data) => {${transform}}`;
  const { error, value } = jsRuntime.evaluateExpression(
    `(${transformFn})(${JSON.stringify(data)})`,
    {}
  );
  if (error) {
    throw error;
  }
  return value;
}

// src/toolpadDataSources/rest/shared.ts
import { ensureSuffix as ensureSuffix2 } from "@mui/toolpad-utils/strings";
var HTTP_NO_BODY = /* @__PURE__ */ new Set(["GET", "HEAD"]);
function getAuthenticationHeaders(auth) {
  if (!auth) {
    return [];
  }
  switch (auth.type) {
    case "basic":
      return [
        [
          "Authorization",
          `Basic ${Buffer.from(`${auth.user}:${auth.password}`, "utf-8").toString("base64")}`
        ]
      ];
    case "bearerToken":
      return [["Authorization", `Bearer ${auth.token}`]];
    case "apiKey":
      return [[auth.header, auth.key]];
    default:
      throw new Error(`Unsupported authentication type "${auth.type}"`);
  }
}
function parseBaseUrl(baseUrl) {
  const parsedBase = new URL(baseUrl);
  parsedBase.pathname = ensureSuffix2(parsedBase.pathname, "/");
  parsedBase.search = "";
  parsedBase.hash = "";
  return parsedBase;
}
function getDefaultUrl(config2, connection) {
  const baseUrl = connection?.baseUrl;
  return baseUrl ? "" : new URL("/static/movies.json", config2.externalUrl || window.location.href).href;
}

// src/toolpadDataSources/rest/server.ts
async function loadEnvFile(project) {
  return project.envManager.getDeclaredValues();
}
function resolveBindable(jsRuntime, bindable, scope) {
  const { value, error } = evaluateBindable(jsRuntime, bindable, scope);
  if (error) {
    throw error;
  }
  return value;
}
function resolveBindableEntries(jsRuntime, entries, scope) {
  return entries.map(([key, value]) => [key, resolveBindable(jsRuntime, value, scope)]);
}
function resolveBindables(jsRuntime, obj, scope) {
  return Object.fromEntries(
    resolveBindableEntries(jsRuntime, Object.entries(obj), scope)
  );
}
function parseQueryUrl(queryUrl, baseUrl) {
  if (baseUrl) {
    const parsedBase = parseBaseUrl(baseUrl);
    return new URL(parsedBase.href + removePrefix(queryUrl, "/"));
  }
  return new URL(queryUrl);
}
function resolveRawBody(jsRuntime, body, scope) {
  const { content, contentType } = resolveBindables(
    jsRuntime,
    {
      contentType: body.contentType,
      content: body.content
    },
    scope
  );
  return {
    kind: "raw",
    contentType,
    content: String(content)
  };
}
function resolveUrlEncodedBody(jsRuntime, body, scope) {
  return {
    kind: "urlEncoded",
    content: resolveBindableEntries(jsRuntime, body.content, scope)
  };
}
function resolveBody(jsRuntime, body, scope) {
  switch (body.kind) {
    case "raw":
      return resolveRawBody(jsRuntime, body, scope);
    case "urlEncoded":
      return resolveUrlEncodedBody(jsRuntime, body, scope);
    default:
      throw new Error(`Missing case for "${body.kind}"`);
  }
}
async function readData(res, fetchQuery) {
  if (!fetchQuery.response || fetchQuery.response?.kind === "json") {
    return res.json();
  }
  if (fetchQuery.response?.kind === "raw") {
    return res.text();
  }
  throw new Error(`Unsupported response type "${fetchQuery.response.kind}"`);
}
async function execBase(project, connection, fetchQuery, params) {
  const har = createHarLog();
  const instrumentedFetch = withHarInstrumentation(fetch3, { har });
  const jsRuntime = createServerJsRuntime(process.env);
  const resolvedParams = resolveBindableEntries(jsRuntime, Object.entries(params), {});
  const queryScope = {
    // @TODO: remove deprecated query after v1
    query: params,
    parameters: Object.fromEntries(resolvedParams)
  };
  const runtimeConfig = await project.getRuntimeConfig();
  const urlvalue = fetchQuery.url || getDefaultUrl(runtimeConfig, connection);
  const resolvedUrl = resolveBindable(jsRuntime, urlvalue, queryScope);
  const resolvedSearchParams = resolveBindableEntries(
    jsRuntime,
    fetchQuery.searchParams || [],
    queryScope
  );
  const resolvedHeaders = resolveBindableEntries(jsRuntime, fetchQuery.headers || [], queryScope);
  const queryUrl = parseQueryUrl(resolvedUrl, connection?.baseUrl);
  resolvedSearchParams.forEach(([key, value]) => queryUrl.searchParams.append(key, value));
  const headers = new Headers([
    ...connection ? getAuthenticationHeaders(connection.authentication) : [],
    ...connection?.headers || []
  ]);
  resolvedHeaders.forEach(([key, value]) => headers.append(key, value));
  const method = fetchQuery.method || "GET";
  const requestInit = { method, headers };
  if (!HTTP_NO_BODY.has(method) && fetchQuery.body) {
    const resolvedBody = resolveBody(jsRuntime, fetchQuery.body, queryScope);
    switch (resolvedBody.kind) {
      case "raw": {
        headers.set("content-type", resolvedBody.contentType);
        requestInit.body = resolvedBody.content;
        break;
      }
      case "urlEncoded": {
        headers.set("content-type", "application/x-www-form-urlencoded");
        requestInit.body = new URLSearchParams(resolvedBody.content).toString();
        break;
      }
      default:
        throw new Error(`Missing case for "${resolvedBody.kind}"`);
    }
  }
  let error;
  let untransformedData;
  let data;
  try {
    const res = await instrumentedFetch(queryUrl.href, requestInit);
    if (!res.ok) {
      throw new Error(`HTTP ${res.status} (${res.statusText}) while fetching "${res.url}"`);
    }
    untransformedData = await readData(res, fetchQuery);
    data = untransformedData;
    if (fetchQuery.transformEnabled && fetchQuery.transform) {
      data = await applyTransform(jsRuntime, fetchQuery.transform, untransformedData);
    }
  } catch (rawError) {
    error = serializeError3(errorFrom6(rawError));
  }
  return { data, untransformedData, error, har };
}
function createDatasource(project) {
  return {
    async exec(connection, fetchQuery, params) {
      const { data, error } = await execBase(project, connection, fetchQuery, params);
      return { data, error };
    },
    async execPrivate(connection, query) {
      switch (query.kind) {
        case "introspection": {
          const env = await loadEnvFile(project);
          return { env };
        }
        case "debugExec":
          return execBase(project, connection, query.query, query.params);
        default:
          throw new Error(`Unknown private query "${query.kind}"`);
      }
    },
    api: {}
  };
}

// src/toolpadDataSources/googleSheets/server.ts
import { drive_v3 } from "@googleapis/drive";
import { sheets_v4 } from "@googleapis/sheets";
import { OAuth2Client } from "google-auth-library";
import { match } from "path-to-regexp";
import { asArray } from "@mui/toolpad-utils/collections";
function createOAuthClient(externalUrl) {
  if (!config_default.googleSheetsClientId) {
    throw new Error('Google Sheets: Missing client ID "TOOLPAD_DATASOURCE_GOOGLESHEETS_CLIENT_ID"');
  }
  if (!config_default.googleSheetsClientSecret) {
    throw new Error(
      'Google Sheets: Missing client secret "TOOLPAD_DATASOURCE_GOOGLESHEETS_CLIENT_SECRET"'
    );
  }
  return new OAuth2Client(
    config_default.googleSheetsClientId,
    config_default.googleSheetsClientSecret,
    new URL("/api/dataSources/googleSheets/auth/callback", externalUrl).href
  );
}
function createDriveClient(client) {
  if (!client) {
    throw new Error("Malformed Google Sheets datasource client");
  }
  return new drive_v3.Drive({
    auth: client
  });
}
function createSheetsClient(client) {
  if (!client) {
    throw new Error("Malformed Google Sheets datasource client");
  }
  return new sheets_v4.Sheets({
    auth: client
  });
}
function createDatasource2(project) {
  const exec = async (connection, query) => {
    const runtimeConfig = await project.getRuntimeConfig();
    const client = createOAuthClient(runtimeConfig.externalUrl);
    if (connection) {
      client.setCredentials(connection);
    }
    const sheets = createSheetsClient(client);
    const { spreadsheetId, sheetName, ranges, headerRow } = query;
    if (spreadsheetId && sheetName) {
      const response = await sheets.spreadsheets.values.get({
        spreadsheetId,
        range: `${sheetName}!${ranges}`
      });
      if (response.status === 200) {
        const { values } = response.data;
        if (values && values.length > 0) {
          let data = values;
          if (headerRow) {
            const firstRow = values.shift() ?? [];
            data = values.map((row) => {
              const rowObject = {};
              row.forEach((elem, cellIndex) => {
                if (firstRow[cellIndex]) {
                  rowObject[firstRow[cellIndex]] = elem;
                }
              });
              return rowObject;
            });
          }
          return { data };
        }
        return { data: [] };
      }
      throw new Error(
        `${response.status}: ${response.statusText} Failed to fetch "${JSON.stringify(query)}"`
      );
    }
    throw new Error(`No sheet selected`);
  };
  const execPrivate = async (connection, query) => {
    const runtimeConfig = await project.getRuntimeConfig();
    const client = createOAuthClient(runtimeConfig.externalUrl);
    if (connection) {
      client.setCredentials(connection);
    }
    if (query.type === "FILE_GET") {
      const driveClient = createDriveClient(client);
      const { spreadsheetId } = query;
      if (spreadsheetId) {
        const response = await driveClient.files.get({
          fileId: spreadsheetId
        });
        if (response.status === 200) {
          return response.data;
        }
        throw new Error(
          `${response?.status}: ${response.statusText} Failed to fetch "${JSON.stringify(query)}"`
        );
      }
      throw new Error(`Google Sheets: Missing spreadsheetId in query`);
    }
    if (query.type === "FILES_LIST") {
      const driveClient = createDriveClient(client);
      const { spreadsheetQuery, pageToken } = query;
      let queryString = "mimeType='application/vnd.google-apps.spreadsheet'";
      if (spreadsheetQuery) {
        const escapedSpreadsheetQuery = spreadsheetQuery.replace(/\\|'/g, "\\$&");
        queryString = `name contains '${escapedSpreadsheetQuery}' and ${queryString}`;
      }
      const response = await driveClient.files.list({
        q: queryString,
        pageToken
      });
      if (response.status === 200) {
        return response.data;
      }
      throw new Error(
        `${response?.status}: ${response.statusText} Failed to fetch "${JSON.stringify(query)}"`
      );
    }
    if (query.type === "FETCH_SPREADSHEET") {
      const sheetsClient = createSheetsClient(client);
      const { spreadsheetId } = query;
      if (spreadsheetId) {
        const response = await sheetsClient.spreadsheets.get({
          spreadsheetId,
          includeGridData: false
        });
        if (response.status === 200) {
          return response.data;
        }
        throw new Error(
          `${response?.status}: ${response.statusText} Failed to fetch "${JSON.stringify(query)}"`
        );
      }
      throw new Error(`Google Sheets: Missing spreadsheetId in query`);
    }
    if (query.type === "CONNECTION_STATUS") {
      const driveClient = createDriveClient(client);
      const response = await driveClient.about.get({ fields: "user" });
      if (response.status === 200) {
        return response.data.user;
      }
      return null;
    }
    if (query.type === "DEBUG_EXEC") {
      return exec(connection, query.query);
    }
    throw new Error(`Google Sheets: Unrecognized private query "${JSON.stringify(query)}"`);
  };
  return {
    exec,
    execPrivate,
    api: {},
    createHandler: () => {
      return async (api, req, res) => {
        const runtimeConfig = await project.getRuntimeConfig();
        const client = createOAuthClient(runtimeConfig.externalUrl);
        try {
          const pathname = `/${asArray(req.query.path).map((segment = "") => encodeURIComponent(String(segment))).join("/")}`;
          const matchAuthLogin = match("/auth/login", { decode: decodeURIComponent });
          const matchAuthCallback = match("/auth/callback", { decode: decodeURIComponent });
          const [state] = asArray(req.query.state);
          if (typeof state !== "string") {
            return res.status(400).send(`Missing query parameter "state"`);
          }
          const { connectionId } = JSON.parse(decodeURIComponent(state));
          const savedConnection = await api.getConnectionParams(connectionId);
          if (savedConnection) {
            client.setCredentials(savedConnection);
          }
          if (matchAuthLogin(pathname)) {
            return res.redirect(
              client.generateAuthUrl({
                access_type: "offline",
                scope: [
                  "https://www.googleapis.com/auth/spreadsheets.readonly",
                  "https://www.googleapis.com/auth/drive.readonly"
                ],
                state,
                include_granted_scopes: true,
                prompt: "consent"
              })
            );
          }
          if (matchAuthCallback(pathname)) {
            const [oAuthError] = asArray(req.query.error);
            if (oAuthError) {
              throw new Error(String(oAuthError));
            }
            const [code] = asArray(req.query.code);
            if (typeof code !== "string") {
              return res.status(400).send(`Missing query parameter "code"`);
            }
            const { tokens, res: getTokenResponse } = await client.getToken(code);
            if (!tokens) {
              throw new Error(`${getTokenResponse?.status}: ${getTokenResponse?.statusText}`);
            }
            if (tokens) {
              client.setCredentials(tokens);
              await api.setConnectionParams(connectionId, client.credentials);
            }
            return res.redirect(`/_toolpad/app/connections/${encodeURIComponent(connectionId)}`);
          }
          return res.status(404).send("No handler exists for given path");
        } catch (e) {
          if (e instanceof Error) {
            console.error(e.message);
            return res.status(500).send(e.message);
          }
          return res.status(500).send(e);
        }
      };
    }
  };
}

// src/toolpadDataSources/local/shared.tsx
function parseLegacyFunctionId(id) {
  if (!id) {
    return { file: "functions.ts" };
  }
  const [file, handler] = id.split("#");
  return handler ? { file, handler } : { file: "functions.ts", handler: file };
}

// src/toolpadDataSources/local/server.ts
function createDatasource3(project) {
  const exec = async (connection, fetchQuery, parameters) => {
    if (!fetchQuery.function) {
      throw new Error("Missing function name");
    }
    const parsed = parseLegacyFunctionId(fetchQuery.function);
    if (!parsed.handler) {
      throw new Error("Missing function name");
    }
    const { data, error } = await project.functionsManager.exec(
      parsed.file,
      parsed.handler,
      parameters
    );
    return { data, error };
  };
  return {
    exec,
    api: {
      async introspection() {
        return project.functionsManager.introspect();
      },
      async debugExec(query, params) {
        return exec(null, query, params);
      },
      async createNew(fileName) {
        return project.functionsManager.createFunctionFile(fileName);
      }
    }
  };
}

// src/toolpadDataSources/server.ts
var dataSources = {
  rest: createDatasource,
  postgres: server_default,
  googleSheets: createDatasource2,
  mysql: server_default2,
  local: createDatasource3
};
var server_default3 = dataSources;

// src/server/DataManager.ts
function withSerializedError(withError) {
  const { error, ...withoutError } = withError;
  return withError.error ? { ...withoutError, error: serializeError4(errorFrom7(error)) } : withoutError;
}
var DataManager = class {
  project;
  dataSources;
  constructor(project) {
    this.project = project;
  }
  getDataSources() {
    if (!this.dataSources) {
      this.dataSources = new Map(
        Object.entries(server_default3).map(([key, value]) => [
          key,
          typeof value === "function" ? value(this.project) : value
        ])
      );
    }
    return this.dataSources;
  }
  async getConnectionParams(connectionId) {
    const dom = await this.project.loadDom();
    const node = getNode(
      dom,
      connectionId,
      "connection"
    );
    return node.attributes.params.$$secret;
  }
  async setConnectionParams(connectionId, params) {
    let dom = await this.project.loadDom();
    const existing = getNode(dom, connectionId, "connection");
    dom = setNodeNamespacedProp(
      dom,
      existing,
      "attributes",
      "params",
      createSecret(params)
    );
    await this.project.saveDom(dom);
  }
  async execDataNodeQuery(dataNode, params) {
    const dataSources2 = this.getDataSources();
    const dataSource = dataNode.attributes.dataSource ? dataSources2.get(dataNode.attributes.dataSource) : void 0;
    if (!dataSource) {
      throw new Error(
        `Unknown datasource "${dataNode.attributes.dataSource}" for query "${dataNode.id}"`
      );
    }
    let result = await dataSource.exec(null, dataNode.attributes.query, params);
    if (isQuery(dataNode)) {
      const transformEnabled = dataNode.attributes.transformEnabled;
      const transform = dataNode.attributes.transform;
      if (transformEnabled && transform) {
        const jsServerRuntime = await createServerJsRuntime2(process.env);
        result = {
          data: await applyTransform(jsServerRuntime, transform, result.data)
        };
      }
    }
    return result;
  }
  async execQuery(pageName, queryName, params) {
    const dom = await this.project.loadDom();
    const page = getPageByName(dom, pageName);
    if (!page) {
      throw new Error(`Unknown page "${pageName}"`);
    }
    const dataNode = getQueryByName(dom, page, queryName);
    if (!dataNode) {
      throw new Error(`Unknown query "${queryName}"`);
    }
    if (!isQuery(dataNode)) {
      throw new Error(`Invalid node type for data request`);
    }
    try {
      const result = await this.execDataNodeQuery(dataNode, params);
      return withSerializedError(result);
    } catch (error) {
      return withSerializedError({ error });
    }
  }
  async dataSourceFetchPrivate(dataSourceId, connectionId, query) {
    const dataSources2 = this.getDataSources();
    const dataSource = dataSources2.get(dataSourceId);
    if (!dataSource) {
      throw new Error(`Unknown dataSource "${dataSourceId}"`);
    }
    if (!dataSource.execPrivate) {
      throw new Error(`No execPrivate available on datasource "${dataSourceId}"`);
    }
    return dataSource.execPrivate(null, query);
  }
  async dataSourceExecPrivate(dataSourceId, method, args) {
    const dataSources2 = this.getDataSources();
    const dataSource = dataSources2.get(dataSourceId);
    if (!dataSource) {
      throw new Error(`Unknown dataSource "${dataSourceId}"`);
    }
    if (!dataSource.api) {
      throw new Error(`No api available on datasource "${dataSourceId}"`);
    }
    return dataSource.api[method](...args);
  }
  createDataHandler() {
    const router = express3.Router();
    router.use(
      cors({
        methods: ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"],
        // TODO: make this configurable
        origin: "*"
      })
    );
    router.post(
      "/:pageName/:queryName",
      express3.json(),
      asyncHandler(async (req, res) => {
        const { pageName, queryName } = req.params;
        invariant2(typeof pageName === "string", "pageName url param required");
        invariant2(typeof queryName === "string", "queryName url variable required");
        const ctx = createServerContext2(req, res);
        const result = await withContext2(ctx, async () => {
          return this.execQuery(pageName, queryName, req.body);
        });
        res.json(result);
      })
    );
    return router;
  }
  createDataSourcesHandler() {
    const dataSources2 = this.getDataSources();
    const router = express3.Router();
    const handlerMap = /* @__PURE__ */ new Map();
    Object.keys(server_default3).forEach((dataSourceId) => {
      const handler = dataSources2.get(dataSourceId)?.createHandler?.();
      if (handler) {
        invariant2(
          typeof handler === "function",
          `Received a "${typeof handler}" instead of a "function" for the "${dataSourceId}" handler`
        );
        handlerMap.set(dataSourceId, handler);
      }
    });
    router.get(
      "/:dataSource/*",
      asyncHandler(async (req, res) => {
        const dataSource = req.params.dataSource;
        if (!dataSource) {
          throw new Error(`Missing path parameter "dataSource"`);
        }
        const handler = handlerMap.get(dataSource);
        if (typeof handler === "function") {
          return handler(this, req, res);
        }
        return res.status(404).json({ message: "No handler found" });
      })
    );
    return router;
  }
};

// src/packageInfo.ts
import * as path6 from "path";
import * as url3 from "node:url";
import * as fs4 from "fs";
var currentDirectory2 = url3.fileURLToPath(new URL(".", import.meta.url));
var pkgJsonContent = fs4.readFileSync(path6.resolve(currentDirectory2, "../../package.json"), {
  encoding: "utf-8"
});
var pkgJson = JSON.parse(pkgJsonContent);
var packageInfo_default = {
  version: pkgJson.version,
  build: process.env.GIT_SHA1?.slice(0, 7) || "dev"
};

// src/server/localMode.ts
invariant3(
  isMainThread,
  "localMode should be used only in the main thread. Use message passing to get data from the main thread."
);
function getThemeFile(root) {
  return path7.join(root, "./theme.yml");
}
function getApplicationFile(root) {
  return path7.join(root, "./application.yml");
}
function getComponentsFolder(root) {
  return path7.join(root, "./components");
}
function getPagesFolder(root) {
  return path7.join(root, "./pages");
}
function getPageFolder(root, name) {
  const pagesFolder = getPagesFolder(root);
  const pageFolder = path7.resolve(pagesFolder, name);
  return pageFolder;
}
function getPageFile(root, name) {
  const pageFolder = getPageFolder(root, name);
  const pageFileName = path7.resolve(pageFolder, "page.yml");
  return pageFileName;
}
function getComponentFilePath(componentsFolder, componentName) {
  return path7.join(componentsFolder, `${componentName}.tsx`);
}
function getOutputFolder(root) {
  return path7.join(root, ".generated");
}
function getAppOutputFolder(root) {
  return path7.join(getOutputFolder(root), "app");
}
async function loadPagesFromFiles(root) {
  const pagesFolder = getPagesFolder(root);
  const entries = await readMaybeDir(pagesFolder);
  const resultEntries = await Promise.all(
    entries.map(async (entry) => {
      if (entry.isDirectory()) {
        const pageName = entry.name;
        const pageDirEntries = new Set(await fs5.readdir(path7.resolve(pagesFolder, pageName)));
        if (pageDirEntries.has("page.yml")) {
          const ymlFilePath = path7.resolve(pagesFolder, pageName, "./page.yml");
          const ymlContent = await readMaybeFile(ymlFilePath);
          if (ymlContent) {
            let parsedFile;
            try {
              parsedFile = yaml.parse(ymlContent);
            } catch (rawError) {
              const error = errorFrom8(rawError);
              console.error(
                `${chalk3.red("error")} - Failed to read page ${chalk3.cyan(pageName)}. ${error.message}`
              );
              return null;
            }
            const result = pageSchema.safeParse(parsedFile);
            if (result.success) {
              return [pageName, result.data];
            }
            console.error(
              `${chalk3.red("error")} - Failed to read page ${chalk3.cyan(pageName)}. ${fromZodError3(
                result.error
              )}`
            );
          }
        }
        const extensions = [".tsx", ".jsx"];
        for (const extension of extensions) {
          if (pageDirEntries.has(`page${extension}`)) {
            return [
              pageName,
              {
                apiVersion: API_VERSION,
                kind: "page",
                spec: {
                  id: pageName,
                  unstable_codeFile: true
                }
              }
            ];
          }
        }
      }
      return null;
    })
  );
  return Object.fromEntries(resultEntries.filter(Boolean));
}
async function loadObjectFromFile(filePath, schema) {
  const content = await readMaybeFile(filePath);
  if (content) {
    const parsedFile = yaml.parse(content);
    const result = schema.safeParse(parsedFile);
    if (result.success) {
      return result.data;
    }
    console.error(
      `${chalk3.red("error")} - Failed to read theme ${chalk3.cyan(filePath)}. ${fromZodError3(
        result.error
      )}`
    );
    return null;
  }
  return null;
}
async function loadThemeFromFile(root) {
  const themeFilePath = getThemeFile(root);
  return loadObjectFromFile(themeFilePath, themeSchema);
}
async function loadApplicationFromFile(root) {
  const applicationFilePath = getApplicationFile(root);
  return loadObjectFromFile(applicationFilePath, applicationSchema);
}
async function createDefaultCodeComponent(name, filePath) {
  const componentId = name.replace(/\s/g, "");
  const propTypeId = `${componentId}Props`;
  const result = await format(
    `
  import * as React from 'react';
  import { Typography } from '@mui/material';
  import { createComponent } from '@mui/toolpad/browser';
  
  export interface ${propTypeId} {
    msg: string;
  }
  
  function ${componentId}({ msg }: ${propTypeId}) {
    return (
      <Typography>{msg}</Typography>
    );
  }

  export default createComponent(${componentId}, {
    argTypes: {
      msg: {
        type: "string",
        default: "Hello world!"
      },
    },
  });    
`,
    filePath
  );
  return result;
}
var Lock = class {
  pending = null;
  async use(doWork) {
    try {
      this.pending = Promise.resolve(this.pending).then(() => doWork());
      return await this.pending;
    } finally {
      this.pending = null;
    }
  }
};
var buildInfoSchema = z3.object({
  timestamp: z3.number(),
  base: z3.string().optional()
});
var DEFAULT_GENERATED_GITIGNORE_FILE_CONTENT = ".generated\n";
async function initGitignore(root) {
  const generatedGitignorePath = path7.resolve(root, ".gitignore");
  if (!await fileExists3(generatedGitignorePath)) {
    console.log(`${chalk3.blue("info")}  - Initializing .gitignore file`);
    await writeFileRecursive2(generatedGitignorePath, DEFAULT_GENERATED_GITIGNORE_FILE_CONTENT, {
      encoding: "utf-8"
    });
  }
}
function mergeThemeIntoAppDom(dom, themeFile) {
  const themeFileSpec = themeFile.spec;
  const app = getApp(dom);
  dom = addNode(
    dom,
    createNode(dom, "theme", {
      theme: themeFileSpec?.options,
      attributes: {}
    }),
    app,
    "themes"
  );
  return dom;
}
function mergeApplicationIntoDom(dom, applicationFile) {
  const applicationFileSpec = applicationFile.spec;
  const app = getApp(dom);
  dom = setNodeNamespacedProp(dom, app, "attributes", "authorization", {
    ...applicationFileSpec?.authorization,
    roles: applicationFileSpec?.authorization?.roles?.map(
      (role) => typeof role === "string" ? { name: role } : role
    )
  });
  return dom;
}
function stringOnly(maybeString) {
  return typeof maybeString === "string" ? maybeString : void 0;
}
function expandChildren(children, dom) {
  return children.sort((child1, child2) => {
    invariant3(child1.parentIndex && child2.parentIndex, "Nodes are not children of another node");
    return compareFractionalIndex(child1.parentIndex, child2.parentIndex);
  }).map((child) => expandFromDom(child, dom));
}
function undefinedWhenEmpty(obj) {
  if (!obj || Object.values(obj).every((property) => property === void 0)) {
    return void 0;
  }
  return obj;
}
function createPageFileQueryFromDomQuery(dataSource, query) {
  switch (dataSource) {
    case "rest": {
      if (!query) {
        return { kind: "rest" };
      }
      query = query;
      let body;
      if (query.body) {
        switch (query.body.kind) {
          case "raw": {
            body = {
              kind: "raw",
              content: query.body.content,
              contentType: query.body.contentType
            };
            break;
          }
          case "urlEncoded": {
            body = {
              kind: "urlEncoded",
              content: query.body.content.map(([name, value]) => ({
                name,
                value
              }))
            };
            break;
          }
          default:
            throw new Error(`Unrecognized body kind "${query.body.kind}"`);
        }
      }
      let response;
      if (query.response) {
        switch (query.response.kind) {
          case "csv": {
            response = { kind: "csv", headers: query.response.headers };
            break;
          }
          case "json": {
            response = { kind: "json" };
            break;
          }
          case "xml": {
            response = { kind: "xml" };
            break;
          }
          case "raw": {
            response = { kind: "raw" };
            break;
          }
          default:
            throw new Error(`Unrecognized response kind "${query.response.kind}"`);
        }
      }
      return {
        kind: "rest",
        url: query.url,
        searchParams: query.searchParams?.map(([name, value]) => ({
          name,
          value
        })),
        headers: query.headers.map(([name, value]) => ({
          name,
          value
        })),
        body,
        method: query.method,
        response,
        transform: query.transform,
        transformEnabled: query.transformEnabled
      };
    }
    case "local":
      if (!query) {
        return { kind: "local" };
      }
      query = query;
      return {
        function: query.function,
        kind: "local"
      };
    default:
      throw new Error(`Unsupported dataSource "${dataSource}"`);
  }
}
function expandFromDom(node, dom) {
  if (isPage(node)) {
    const children = getChildNodes(dom, node);
    return {
      apiVersion: API_VERSION,
      kind: "page",
      spec: {
        displayName: node.attributes.displayName,
        alias: node.attributes.alias,
        title: node.attributes.title,
        parameters: undefinedWhenEmpty(
          node.attributes.parameters?.map(([name, value]) => ({ name, value })) ?? []
        ),
        content: undefinedWhenEmpty(expandChildren(children.children || [], dom)),
        queries: undefinedWhenEmpty(expandChildren(children.queries || [], dom)),
        display: node.attributes.display,
        unstable_codeFile: node.attributes.codeFile,
        authorization: node.attributes.authorization
      }
    };
  }
  if (isQuery(node)) {
    return {
      name: node.name,
      enabled: node.attributes.enabled,
      mode: node.attributes.mode,
      query: node.attributes.dataSource ? createPageFileQueryFromDomQuery(
        node.attributes.dataSource,
        node.attributes.query
      ) : void 0,
      parameters: undefinedWhenEmpty(node.params?.map(([name, value]) => ({ name, value }))),
      cacheTime: node.attributes.cacheTime,
      refetchInterval: node.attributes.refetchInterval,
      transform: node.attributes.transform,
      transformEnabled: node.attributes.transformEnabled
    };
  }
  if (isElement(node)) {
    const { children, ...templates } = getChildNodes(dom, node);
    const templateProps = mapValues(
      templates,
      (subtree) => subtree ? {
        $$template: expandChildren(subtree, dom)
      } : void 0
    );
    return {
      component: node.attributes.component,
      name: node.name,
      layout: undefinedWhenEmpty({
        columnSize: node.layout?.columnSize,
        horizontalAlign: stringOnly(node.layout?.horizontalAlign),
        verticalAlign: stringOnly(node.layout?.verticalAlign)
      }),
      props: undefinedWhenEmpty({ ...node.props, ...templateProps }),
      children: undefinedWhenEmpty(expandChildren(children || [], dom))
    };
  }
  throw new Error(`Unsupported node type "${node.type}"`);
}
function isTemplate(bindableProp) {
  return !!(bindableProp && typeof bindableProp === "object" && hasOwnProperty2(bindableProp, "$$template"));
}
function mergeElementIntoDom(dom, parent, parentProp, elm) {
  const plainProps = filterValues(elm.props ?? {}, (prop) => !isTemplate(prop));
  const templateProps = filterValues(elm.props ?? {}, isTemplate);
  const elmNode = createElement(dom, elm.component, plainProps, elm.layout ?? {}, elm.name);
  dom = addNode(dom, elmNode, parent, parentProp);
  if (elm.children) {
    for (const child of elm.children) {
      dom = mergeElementIntoDom(dom, elmNode, "children", child);
    }
  }
  for (const [propName, templateProp] of Object.entries(templateProps)) {
    for (const child of templateProp.$$template) {
      dom = mergeElementIntoDom(dom, elmNode, propName, child);
    }
  }
  return dom;
}
function createDomQueryFromPageFileQuery(query) {
  switch (query.kind) {
    case "local":
      return {
        function: query.function
      };
    case "rest": {
      let body;
      if (query.body) {
        switch (query.body.kind) {
          case "raw": {
            body = {
              kind: "raw",
              content: query.body.content,
              contentType: query.body.contentType
            };
            break;
          }
          case "urlEncoded": {
            body = {
              kind: "urlEncoded",
              content: query.body.content.map(({ name, value }) => [
                name,
                value
              ])
            };
            break;
          }
          default:
            throw new Error(`Unrecognized body kind "${query.body.kind}"`);
        }
      }
      let response;
      if (query.response) {
        switch (query.response.kind) {
          case "csv": {
            response = { kind: "csv", headers: query.response.headers };
            break;
          }
          case "json": {
            response = { kind: "json" };
            break;
          }
          case "xml": {
            response = { kind: "xml" };
            break;
          }
          case "raw": {
            response = { kind: "raw" };
            break;
          }
          default:
            throw new Error(`Unrecognized response kind "${query.response.kind}"`);
        }
      }
      return {
        url: query.url || void 0,
        headers: query.headers?.map(({ name, value }) => [name, value]) || [],
        method: query.method || "GET",
        browser: false,
        transform: query.transform,
        transformEnabled: query.transformEnabled,
        searchParams: query.searchParams?.map(({ name, value }) => [name, value]) || [],
        body,
        response
      };
    }
    default:
      throw new Error(`Unrecognized query kind "${query.kind}"`);
  }
}
function createPageDomFromPageFile(pageName, pageFile) {
  const pageFileSpec = pageFile.spec ?? {};
  let fragment = createFragment("page", {
    name: pageName,
    attributes: {
      displayName: pageFileSpec.displayName,
      // Convert deprecated id to alias
      alias: pageFileSpec.id ? [pageFileSpec.id] : pageFileSpec.alias,
      title: pageFileSpec.title,
      parameters: pageFileSpec.parameters?.map(({ name, value }) => [name, value]) || [],
      display: pageFileSpec.display || void 0,
      codeFile: pageFileSpec.unstable_codeFile || void 0,
      authorization: pageFileSpec.authorization || void 0
    }
  });
  const pageNode = getRoot(fragment);
  assertIsPage(pageNode);
  if (pageFileSpec.queries) {
    for (const query of pageFileSpec.queries) {
      if (query.query) {
        const queryNode = createNode(fragment, "query", {
          name: query.name,
          attributes: {
            connectionId: null,
            dataSource: typeof query.query?.kind === "string" ? query.query.kind : void 0,
            query: createDomQueryFromPageFileQuery(query.query),
            cacheTime: typeof query.cacheTime === "number" ? query.cacheTime : void 0,
            enabled: query.enabled ?? void 0,
            mode: typeof query.mode === "string" ? query.mode : void 0,
            transform: typeof query.transform === "string" ? query.transform : void 0,
            refetchInterval: typeof query.refetchInterval === "number" ? query.refetchInterval : void 0,
            transformEnabled: query.transformEnabled ?? void 0
          },
          params: query.parameters?.map(
            ({ name, value }) => [name, value]
          )
        });
        fragment = addNode(fragment, queryNode, pageNode, "queries");
      }
    }
  }
  if (pageFileSpec.content) {
    for (const child of pageFileSpec.content) {
      fragment = mergeElementIntoDom(fragment, pageNode, "children", child);
    }
  }
  return fragment;
}
function mergePageIntoDom(dom, pageName, pageFile) {
  const appRoot = getRoot(dom);
  const pageFragment = createPageDomFromPageFile(pageName, pageFile);
  const newPageNode = getRoot(pageFragment);
  if (getMaybeNode(dom, newPageNode.id)) {
    dom = removeNode(dom, newPageNode.id);
  }
  dom = addFragment(dom, pageFragment, appRoot.id, "pages");
  return dom;
}
function optimizePageElement(element) {
  const isLayoutElement = (possibleLayoutElement) => possibleLayoutElement.component === PAGE_ROW_COMPONENT_ID || possibleLayoutElement.component === PAGE_COLUMN_COMPONENT_ID;
  if (isLayoutElement(element) && element.children?.length === 1) {
    const onlyChild = element.children[0];
    if (!isLayoutElement(onlyChild)) {
      return optimizePageElement({
        ...onlyChild,
        layout: {
          ...onlyChild.layout,
          columnSize: 1
        }
      });
    }
  }
  return {
    ...element,
    children: element.children && element.children.map(optimizePageElement)
  };
}
function optimizePage(page) {
  return {
    ...page,
    spec: {
      ...page.spec,
      content: page.spec?.content?.map(optimizePageElement)
    }
  };
}
function mergePagesIntoDom(dom, pages) {
  for (const [name, page] of Object.entries(pages)) {
    dom = mergePageIntoDom(dom, name, optimizePage(page));
  }
  return dom;
}
function extractPagesFromDom(dom) {
  const rootNode = getApp(dom);
  const { pages: pageNodes = [] } = getChildNodes(dom, rootNode);
  const pages = {};
  for (const pageNode of pageNodes) {
    pages[pageNode.name] = expandFromDom(pageNode, dom);
    dom = removeNode(dom, pageNode.id);
  }
  return { pages, dom };
}
function extractThemeFromDom(dom) {
  const rootNode = getApp(dom);
  const { themes: themeNodes = [] } = getChildNodes(dom, rootNode);
  if (themeNodes.length > 0) {
    return {
      apiVersion: API_VERSION,
      kind: "theme",
      spec: {
        options: themeNodes[0].theme
      }
    };
  }
  return null;
}
function extractApplicationFromDom(dom) {
  const rootNode = getApp(dom);
  return {
    apiVersion: API_VERSION,
    kind: "application",
    spec: {
      authorization: rootNode.attributes.authorization
    }
  };
}
function getSchemaUrl(obj) {
  return `https://raw.githubusercontent.com/mui/mui-toolpad/v${packageInfo_default.version}/docs/schemas/v1/definitions.json#properties/${obj}`;
}
async function writePagesToFiles(root, pages) {
  await Promise.all(
    Object.entries(pages).map(async ([name, page]) => {
      const pageFileName = getPageFile(root, name);
      await updateYamlFile(pageFileName, optimizePage(page), {
        schemaUrl: getSchemaUrl("Page")
      });
    })
  );
}
async function writeThemeFile(root, theme) {
  const themeFilePath = getThemeFile(root);
  if (theme) {
    await updateYamlFile(themeFilePath, theme, {
      schemaUrl: getSchemaUrl("Theme")
    });
  } else {
    await fs5.rm(themeFilePath, { recursive: true, force: true });
  }
}
async function writeApplicationFile(root, application) {
  const applicationFilePath = getApplicationFile(root);
  if (application) {
    await updateYamlFile(applicationFilePath, application);
  } else {
    await fs5.rm(applicationFilePath, { recursive: true, force: true });
  }
}
async function writeDomToDisk(root, dom) {
  const { pages: pagesContent } = extractPagesFromDom(dom);
  await Promise.all([
    writePagesToFiles(root, pagesContent),
    writeThemeFile(root, extractThemeFromDom(dom)),
    writeApplicationFile(root, extractApplicationFromDom(dom))
  ]);
}
async function findSupportedEditor() {
  if (process.env.EDITOR) {
    return void 0;
  }
  try {
    await execa("code", ["-v"]);
    return "code";
  } catch (err) {
    return void 0;
  }
}
async function readProjectFolder(root) {
  const [pagesContent, theme, application] = await Promise.all([
    loadPagesFromFiles(root),
    loadThemeFromFile(root),
    loadApplicationFromFile(root)
  ]);
  return {
    application,
    pages: pagesContent,
    theme
  };
}
async function writeProjectFolder(root, folder) {
  await Promise.all([
    writePagesToFiles(root, folder.pages),
    writeThemeFile(root, folder.theme),
    writeApplicationFile(root, folder.application)
  ]);
}
function projectFolderToAppDom(projectFolder) {
  let dom = createDom();
  dom = mergePagesIntoDom(dom, projectFolder.pages);
  if (projectFolder.theme) {
    dom = mergeThemeIntoAppDom(dom, projectFolder.theme);
  }
  if (projectFolder.application) {
    dom = mergeApplicationIntoDom(dom, projectFolder.application);
  }
  return dom;
}
async function loadProjectFolder(root) {
  return readProjectFolder(root);
}
async function loadDomFromDisk(root) {
  const projectFolder = await loadProjectFolder(root);
  return projectFolderToAppDom(projectFolder);
}
function getDomFilePatterns(root) {
  return [
    path7.resolve(root, "./pages/*/page.yml"),
    path7.resolve(root, "./theme.yml"),
    path7.resolve(root, "./application.yml")
  ];
}
async function calculateDomFingerprint(root) {
  const files = await glob2(getDomFilePatterns(root), { windowsPathsNoEscape: true });
  const mtimes = await Promise.all(
    files.sort().map(async (file) => {
      const stats = await fs5.stat(file);
      return [file, stats.mtimeMs];
    })
  );
  return insecureHash(JSON.stringify(mtimes));
}
var ToolpadProject = class {
  root;
  events = new Emitter();
  domAndFingerprint = null;
  domAndFingerprintLock = new Lock();
  options;
  envManager;
  functionsManager;
  dataManager;
  invalidateQueries;
  alertedMissingVars = /* @__PURE__ */ new Set();
  lastVersionCheck = 0;
  pendingVersionCheck;
  componentsManifestPromise;
  pagesManifestPromise;
  constructor(root, options) {
    invariant3(
      // eslint-disable-next-line no-underscore-dangle
      !global.__toolpadProjects?.has(root),
      `A project is already running for "${root}"`
    );
    global.__toolpadProjects ??= /* @__PURE__ */ new Set();
    global.__toolpadProjects.add(root);
    this.root = root;
    this.options = options;
    this.envManager = new EnvManager(this);
    this.functionsManager = new FunctionsManager(this);
    this.dataManager = new DataManager(this);
    this.invalidateQueries = throttle(
      () => {
        this.events.emit("queriesInvalidated", {});
      },
      250,
      {
        leading: false
      }
    );
  }
  initWatcher() {
    if (!this.options.dev) {
      return;
    }
    const updateDomFromExternal = debounce(() => {
      this.domAndFingerprintLock.use(async () => {
        const [, fingerprint] = await this.loadDomAndFingerprint();
        const newFingerprint = await calculateDomFingerprint(this.root);
        if (fingerprint !== newFingerprint) {
          console.log(`${chalk3.magenta("event")} - Project changed on disk, updating...`);
          this.domAndFingerprint = await Promise.all([
            loadDomFromDisk(this.root),
            calculateDomFingerprint(this.root)
          ]);
          this.events.emit("change", {});
          this.events.emit("externalChange", {});
        }
      });
    }, 100);
    const watchOptions = {
      // This is needed to correctly pick up page folder renames
      // Remove this once https://github.com/paulmillr/chokidar/issues/1285 gets resolved
      usePolling: true
    };
    chokidar3.watch(getDomFilePatterns(this.root), watchOptions).on("all", () => {
      updateDomFromExternal();
    });
    const handleComponentFileChange = async () => {
      const oldManifest = await this.componentsManifestPromise;
      this.componentsManifestPromise = this.buildComponentsManifest();
      const newManifest = await this.componentsManifestPromise;
      if (JSON.stringify(oldManifest) !== JSON.stringify(newManifest)) {
        this.events.emit("componentsListChanged", {});
      }
    };
    chokidar3.watch(
      [path7.resolve(this.root, "./components"), path7.resolve(this.root, "./components/*.*")],
      watchOptions
    ).on("add", handleComponentFileChange).on("addDir", handleComponentFileChange).on("unlink", handleComponentFileChange).on("unlinkDir", handleComponentFileChange);
    const handlePageFileChange = async () => {
      const oldManifest = await this.pagesManifestPromise;
      this.pagesManifestPromise = buildPagesManifest(this.root);
      const newManifest = await this.pagesManifestPromise;
      if (JSON.stringify(oldManifest) !== JSON.stringify(newManifest)) {
        this.events.emit("pagesManifestChanged", {});
      }
    };
    chokidar3.watch(
      [path7.resolve(this.root, "./pages"), path7.resolve(this.root, "./pages/*/page.*")],
      watchOptions
    ).on("add", handlePageFileChange).on("addDir", handlePageFileChange).on("unlink", handlePageFileChange).on("unlinkDir", handlePageFileChange);
  }
  async loadDomAndFingerprint() {
    if (!this.domAndFingerprint) {
      this.domAndFingerprint = Promise.all([
        loadDomFromDisk(this.root),
        calculateDomFingerprint(this.root)
      ]);
    }
    return this.domAndFingerprint;
  }
  getRoot() {
    return this.root;
  }
  getOutputFolder() {
    return getOutputFolder(this.getRoot());
  }
  getAppOutputFolder() {
    return getAppOutputFolder(this.getRoot());
  }
  getBuildInfoFile() {
    return path7.resolve(this.getOutputFolder(), "buildInfo.json");
  }
  alertOnMissingVariablesInDom(dom) {
    const requiredVars = getRequiredEnvVars(dom);
    const missingVars = Array.from(requiredVars).filter(
      (key) => typeof process.env[key] === "undefined"
    );
    const toAlert = missingVars.filter((key) => !this.alertedMissingVars.has(key));
    if (toAlert.length > 0) {
      const firstThree = toAlert.slice(0, 3);
      const restCount = toAlert.length - firstThree.length;
      const missingListMsg = firstThree.map((varName) => chalk3.cyan(varName)).join(", ");
      const restMsg = restCount > 0 ? ` and ${restCount} more` : "";
      console.log(
        `${chalk3.yellow(
          "warn"
        )}  - Missing required environment variable(s): ${missingListMsg}${restMsg}.`
      );
    }
    this.alertedMissingVars = new Set(missingVars);
  }
  async start() {
    if (this.options.dev) {
      await this.resetBuildInfo();
      await this.initWatcher();
    } else {
      const buildInfo = await this.getBuildInfo();
      if (!buildInfo) {
        throw new Error(`No production build found. Please run "toolpad build" first.`);
      }
      if (buildInfo.base !== this.options.base) {
        throw new Error(
          `Production build found for base "${buildInfo.base}" but running the app with "${this.options.base}". Please run "toolpad build" with the correct --base option.`
        );
      }
    }
    await Promise.all([this.envManager.start(), this.functionsManager.start()]);
  }
  async build() {
    await Promise.all([this.envManager.build(), this.functionsManager.build()]);
  }
  async dispose() {
    await Promise.all([this.envManager.dispose(), this.functionsManager.dispose()]);
    global.__toolpadProjects?.delete(this.root);
  }
  async loadDom() {
    const [dom] = await this.loadDomAndFingerprint();
    this.alertOnMissingVariablesInDom(dom);
    return dom;
  }
  async buildComponentsManifest() {
    const componentsFolder = getComponentsFolder(this.getRoot());
    const entries = await readMaybeDir(componentsFolder) || [];
    const result = entries.map((entry) => {
      if (entry.isFile()) {
        const fileName = entry.name;
        const componentName = entry.name.replace(/\.tsx$/, "");
        const filePath = path7.resolve(componentsFolder, fileName);
        return { name: componentName, path: filePath };
      }
      return null;
    });
    return result.filter(Boolean);
  }
  async getComponentsManifest() {
    if (!this.componentsManifestPromise) {
      this.componentsManifestPromise = this.buildComponentsManifest();
    }
    return this.componentsManifestPromise;
  }
  async writeDomToDisk(newDom) {
    if (!this.options.dev) {
      throw new Error(`Writing to disk is only possible in toolpad dev mode.`);
    }
    await writeDomToDisk(this.root, newDom);
    const newFingerprint = await calculateDomFingerprint(this.root);
    this.domAndFingerprint = [newDom, newFingerprint];
    this.events.emit("change", { fingerprint: newFingerprint });
  }
  async init() {
    const projectFolder = await readProjectFolder(this.root);
    if (Object.keys(projectFolder.pages).length <= 0) {
      projectFolder.pages.page = {
        apiVersion: API_VERSION,
        kind: "page",
        spec: {
          id: createId(),
          title: "Default page"
        }
      };
      await writeProjectFolder(this.root, projectFolder);
    }
    await initGitignore(this.root);
  }
  async saveDom(newDom) {
    await this.domAndFingerprintLock.use(async () => {
      return this.writeDomToDisk(newDom);
    });
  }
  async applyDomDiff(domDiff) {
    await this.domAndFingerprintLock.use(async () => {
      const dom = await this.loadDom();
      const newDom = applyDiff(dom, domDiff);
      return this.writeDomToDisk(newDom);
    });
  }
  async openCodeEditor(fileName, fileType) {
    const supportedEditor = await findSupportedEditor();
    const root = this.getRoot();
    let resolvedPath = fileName;
    if (fileType === "resource") {
      resolvedPath = await this.functionsManager.getFunctionFilePath(fileName);
    }
    if (fileType === "component") {
      const componentsFolder = getComponentsFolder(root);
      resolvedPath = getComponentFilePath(componentsFolder, fileName);
    }
    const fullResolvedPath = path7.resolve(root, resolvedPath);
    await openEditor([fullResolvedPath, root], {
      editor: supportedEditor
    });
  }
  async getVersionInfo() {
    const now = Date.now();
    if (!this.pendingVersionCheck || this.lastVersionCheck + VERSION_CHECK_INTERVAL <= now) {
      this.lastVersionCheck = now;
      this.pendingVersionCheck = checkVersion(this.root);
    }
    return this.pendingVersionCheck;
  }
  async createComponent(name) {
    const componentsFolder = getComponentsFolder(this.root);
    const filePath = getComponentFilePath(componentsFolder, name);
    const content = await createDefaultCodeComponent(name, filePath);
    await writeFileRecursive2(filePath, content, { encoding: "utf-8" });
  }
  async createDataProvider(name, options) {
    return this.functionsManager.createDataProviderFile(name, options);
  }
  async deletePage(name) {
    const pageFolder = getPageFolder(this.root, name);
    await fs5.rm(pageFolder, { force: true, recursive: true });
  }
  async getPrettierConfig() {
    const root = this.getRoot();
    const config2 = await resolvePrettierConfig(root);
    return config2;
  }
  async getRuntimeConfig() {
    invariant3(this.options.externalUrl, "External URL is not set");
    return {
      externalUrl: this.options.externalUrl
    };
  }
  async writeBuildInfo() {
    await writeFileRecursive2(
      this.getBuildInfoFile(),
      JSON.stringify({
        timestamp: Date.now(),
        base: this.options.base
      }),
      { encoding: "utf-8" }
    );
  }
  async resetBuildInfo() {
    await fs5.rm(this.getBuildInfoFile(), { force: true, recursive: true });
  }
  async getBuildInfo() {
    try {
      const content = await readJsonFile2(this.getBuildInfoFile());
      return buildInfoSchema.parse(content);
    } catch {
      return null;
    }
  }
  async getPagesManifest() {
    if (!this.pagesManifestPromise) {
      this.pagesManifestPromise = buildPagesManifest(this.root);
    }
    return this.pagesManifestPromise;
  }
};
function resolveProjectDir(dir) {
  const projectDir = path7.resolve(process.cwd(), dir);
  return projectDir;
}
async function initProject({ dir: dirInput, ...config2 }) {
  const dir = resolveProjectDir(dirInput);
  const resolvedConfig = {
    dev: false,
    base: "/prod",
    customServer: false,
    ...config2
  };
  const project = new ToolpadProject(dir, resolvedConfig);
  await project.init();
  return project;
}
var basePagesManifestEntrySchema = z3.object({
  slug: z3.string(),
  title: z3.string(),
  legacy: z3.boolean().optional()
});
var pagesManifestEntrySchema = basePagesManifestEntrySchema.extend(
  {
    children: z3.array(z3.lazy(() => pagesManifestEntrySchema))
  }
);
var pagesManifestSchema = z3.object({
  pages: z3.array(pagesManifestEntrySchema)
});
async function buildPagesManifest(root) {
  const pagesFolder = getPagesFolder(root);
  const pageDirs = await readMaybeDir(pagesFolder);
  const pages = (await Promise.all(
    pageDirs.map(async (page) => {
      if (page.isDirectory()) {
        const pagePath = path7.resolve(pagesFolder, page.name);
        const title = guessTitle(page.name);
        const extensions = [".tsx", ".jsx"];
        for (const extension of extensions) {
          const pageFilePath2 = path7.resolve(pagePath, `page${extension}`);
          const stat3 = await fs5.stat(pageFilePath2).catch(() => null);
          if (stat3?.isFile()) {
            return [
              {
                slug: page.name,
                title,
                children: []
              }
            ];
          }
        }
        const pageFilePath = path7.resolve(pagePath, "page.yml");
        const stat2 = await fs5.stat(pageFilePath).catch(() => null);
        if (stat2?.isFile()) {
          return [
            {
              slug: page.name,
              title,
              legacy: true,
              children: []
            }
          ];
        }
      }
      return [];
    })
  )).flat();
  pages.sort((page1, page2) => page1.title.localeCompare(page2.title));
  return { pages };
}

// src/server/projectRpcServer.ts
function createRpcServer2(project) {
  return {
    dataSourceFetchPrivate: createMethod(
      ({ params }) => {
        return project.dataManager.dataSourceFetchPrivate(...params);
      }
    ),
    execQuery: createMethod(({ params }) => {
      return project.dataManager.execDataNodeQuery(...params);
    }),
    loadDom: createMethod(({ params }) => {
      return project.loadDom(...params);
    }),
    getVersionInfo: createMethod(({ params }) => {
      return project.getVersionInfo(...params);
    }),
    introspect: createMethod(({ params }) => {
      return project.functionsManager.introspect(...params);
    }),
    getPrettierConfig: createMethod(({ params }) => {
      return project.getPrettierConfig(...params);
    }),
    saveDom: createMethod(({ params }) => {
      return project.saveDom(...params);
    }),
    applyDomDiff: createMethod(({ params }) => {
      return project.applyDomDiff(...params);
    }),
    openCodeEditor: createMethod(({ params }) => {
      return project.openCodeEditor(...params);
    }),
    createComponent: createMethod(({ params }) => {
      return project.createComponent(...params);
    }),
    deletePage: createMethod(({ params }) => {
      return project.deletePage(...params);
    }),
    dataSourceExecPrivate: createMethod(
      ({ params }) => {
        return project.dataManager.dataSourceExecPrivate(...params);
      }
    ),
    createDataProvider: createMethod(({ params }) => {
      return project.createDataProvider(...params);
    }),
    getRuntimeConfig: createMethod(({ params }) => {
      return project.getRuntimeConfig(...params);
    }),
    getComponents: createMethod(({ params }) => {
      return project.getComponentsManifest(...params);
    })
  };
}

// src/server/index.ts
import.meta.url ??= url4.pathToFileURL(__filename).toString();
var currentDirectory3 = url4.fileURLToPath(new URL(".", import.meta.url));
async function createDevHandler(project) {
  const handler = express4.Router();
  handler.use((req, res, next) => {
    res.setHeader("X-Toolpad-Base", project.options.base);
    next();
  });
  handler.use(cors2());
  const appServerPath = path8.resolve(currentDirectory3, "../cli/appServerWorker.mjs");
  const [wsPort, devPort, runtimeConfig] = await Promise.all([
    getPort(),
    getPort(),
    project.getRuntimeConfig()
  ]);
  const mainThreadRpcChannel = new MessageChannel();
  const worker = new Worker(appServerPath, {
    workerData: {
      outDir: project.getAppOutputFolder(),
      base: project.options.base,
      config: runtimeConfig,
      root: project.getRoot(),
      port: devPort,
      mainThreadRpcPort: mainThreadRpcChannel.port1,
      customServer: project.options.customServer
    },
    transferList: [mainThreadRpcChannel.port1],
    env: {
      ...process.env,
      NODE_ENV: "development"
    }
  });
  worker.once("exit", (code) => {
    console.error(`App dev server failed ${code}`);
    process.exit(1);
  });
  let resolveReadyPromise;
  const readyPromise = new Promise((resolve9) => {
    resolveReadyPromise = resolve9;
  });
  serveRpc(mainThreadRpcChannel.port2, {
    notifyReady: async () => resolveReadyPromise?.(),
    loadDom: async () => project.loadDom(),
    getComponents: async () => project.getComponentsManifest(),
    getPagesManifest: async () => project.getPagesManifest()
  });
  project.events.on("componentsListChanged", () => {
    worker.postMessage({ kind: "reload-components" });
  });
  const rpcServer = createRpcServer2(project);
  handler.use("/__toolpad_dev__/rpc", createRpcHandler(rpcServer));
  handler.use(
    "/__toolpad_dev__/reactDevtools",
    express4.static(path8.resolve(currentDirectory3, "../../dist/reactDevtools"))
  );
  handler.use(
    "/__toolpad_dev__/manifest.json",
    asyncHandler(async (req, res) => {
      const wsProtocol = req.protocol === "http" ? "ws" : "wss";
      res.json({
        rootDir: project.getRoot(),
        wsUrl: `${wsProtocol}://${req.hostname}:${wsPort}`
      });
    })
  );
  handler.use("/api/data", project.dataManager.createDataHandler());
  const runtimeRpcServer = createRpcServer(project);
  handler.use("/api/runtime-rpc", createRpcHandler(runtimeRpcServer));
  handler.use(
    (req, res, next) => {
      readyPromise.then(next, next);
    },
    createProxyMiddleware({
      logLevel: "silent",
      ws: true,
      target: {
        host: "localhost",
        port: devPort
      }
    })
  );
  const wsServer = new WebSocketServer({ port: wsPort });
  project.events.on("*", (event, payload) => {
    wsServer.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify({ kind: "projectEvent", event, payload }));
      }
    });
  });
  wsServer.on("connection", (ws, _request) => {
    ws.on("error", console.error);
  });
  return {
    handler,
    async dispose() {
      worker.postMessage({ kind: "exit" });
    }
  };
}
async function createToolpadAppHandler(project) {
  const appHandler = project.options.dev ? await createDevHandler(project) : await createProdHandler(project);
  return appHandler;
}
async function createHandler({
  dev = false,
  dir = "./toolpad",
  base = "/prod",
  externalUrl = "http://localhost:3000"
}) {
  const project = await initProject({ dev, dir, externalUrl, base, customServer: true });
  await project.start();
  const appHandler = await createToolpadAppHandler(project);
  return {
    handler: appHandler.handler,
    dispose: async () => {
      await Promise.allSettled([project.dispose(), appHandler.dispose()]);
    }
  };
}
export {
  createHandler
};
//# sourceMappingURL=index.mjs.map