UNPKG

@getcronit/pylon

Version:

![Pylon cover](https://github.com/user-attachments/assets/c28e49b2-5672-4849-826e-8b2eab0360cc)

2,524 lines 91.9 kB
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
  if (typeof require !== "undefined") return require.apply(this, arguments);
  throw Error('Dynamic require of "' + x + '" is not supported');
});

// src/define-pylon.ts
import * as Sentry from "@sentry/bun";
import consola from "consola";
import {
  GraphQLError
} from "graphql";

// src/context.ts
import { AsyncLocalStorage } from "async_hooks";
import { env } from "hono/adapter";
var asyncContext = new AsyncLocalStorage();
var getContext = () => {
  const ctx = asyncContext.getStore();
  if (!ctx) {
    throw new Error("Context not defined");
  }
  ctx.env = env(ctx);
  return ctx;
};
var setContext = (context) => {
  return asyncContext.enterWith(context);
};

// src/define-pylon.ts
import { isAsyncIterable } from "graphql-yoga";
function getAllPropertyNames(instance) {
  const allProps = /* @__PURE__ */ new Set();
  let currentObj = instance;
  while (currentObj && currentObj !== Object.prototype) {
    const ownProps = Object.getOwnPropertyNames(currentObj);
    ownProps.forEach((prop) => allProps.add(prop));
    currentObj = Object.getPrototypeOf(currentObj);
  }
  return Array.from(allProps).filter((prop) => prop !== "constructor");
}
async function wrapFunctionsRecursively(obj, wrapper, that = null, selectionSet = [], info) {
  if (obj === null || obj instanceof Date) {
    return obj;
  }
  if (Array.isArray(obj)) {
    return await Promise.all(
      obj.map(async (item) => {
        return await wrapFunctionsRecursively(
          item,
          wrapper,
          that,
          selectionSet,
          info
        );
      })
    );
  } else if (typeof obj === "function") {
    return Sentry.startSpan(
      {
        name: obj.name,
        op: "pylon.fn"
      },
      async () => {
        return await wrapper.call(that, obj, selectionSet, info);
      }
    );
  } else if (obj instanceof Promise) {
    return await wrapFunctionsRecursively(
      await obj,
      wrapper,
      that,
      selectionSet,
      info
    );
  } else if (isAsyncIterable(obj)) {
    return obj;
  } else if (typeof obj === "object") {
    that = obj;
    const result = {};
    for (const key of getAllPropertyNames(obj)) {
      result[key] = await wrapFunctionsRecursively(
        obj[key],
        wrapper,
        that,
        selectionSet,
        info
      );
    }
    return result;
  } else {
    return await obj;
  }
}
function spreadFunctionArguments(fn) {
  return (otherArgs, c, info) => {
    const selections = arguments[1];
    const realInfo = arguments[2];
    let args = {};
    if (info) {
      const type = info.parentType;
      const field = type.getFields()[info.fieldName];
      const fieldArguments = field?.args;
      const preparedArguments = fieldArguments?.reduce(
        (acc, arg) => {
          if (otherArgs[arg.name] !== void 0) {
            acc[arg.name] = otherArgs[arg.name];
          } else {
            acc[arg.name] = void 0;
          }
          return acc;
        },
        {}
      );
      if (preparedArguments) {
        args = preparedArguments;
      }
    } else {
      args = otherArgs;
    }
    const orderedArgs = Object.keys(args).map((key) => args[key]);
    const that = this || {};
    const result = wrapFunctionsRecursively(
      fn.call(that, ...orderedArgs),
      spreadFunctionArguments,
      this,
      selections,
      realInfo
    );
    return result;
  };
}
var resolversToGraphQLResolvers = (resolvers, configureContext) => {
  const rootGraphqlResolver = (fn) => async (_, args, ctx, info) => {
    return Sentry.withScope(async (scope) => {
      const ctx2 = asyncContext.getStore();
      if (!ctx2) {
        consola.warn(
          "Context is not defined. Make sure AsyncLocalStorage is supported in your environment."
        );
      }
      ctx2?.set("graphqlResolveInfo", info);
      const auth = ctx2?.get("auth");
      if (auth?.user) {
        scope.setUser({
          id: auth.user.sub,
          username: auth.user.preferred_username,
          email: auth.user.email,
          details: auth.user
        });
      }
      let type = null;
      switch (info.operation.operation) {
        case "query":
          type = info.schema.getQueryType();
          break;
        case "mutation":
          type = info.schema.getMutationType();
          break;
        case "subscription":
          type = info.schema.getSubscriptionType();
          break;
        default:
          throw new Error("Unknown operation");
      }
      const field = type?.getFields()[info.fieldName];
      const fieldArguments = field?.args || [];
      const preparedArguments = fieldArguments.reduce(
        (acc, arg) => {
          if (args[arg.name] !== void 0) {
            acc[arg.name] = args[arg.name];
          } else {
            acc[arg.name] = void 0;
          }
          return acc;
        },
        {}
      );
      let inner = await fn;
      let baseSelectionSet = [];
      for (const selection of info.operation.selectionSet.selections) {
        if (selection.kind === "Field" && selection.name.value === info.fieldName) {
          baseSelectionSet = selection.selectionSet?.selections || [];
        }
      }
      const wrappedFn = await wrapFunctionsRecursively(
        inner,
        spreadFunctionArguments,
        void 0,
        baseSelectionSet,
        info
      );
      if (typeof wrappedFn !== "function") {
        return wrappedFn;
      }
      const res = await wrappedFn(preparedArguments);
      return res;
    });
  };
  const graphqlResolvers = {};
  for (const key of Object.keys(resolvers.Query)) {
    if (!resolvers.Query[key]) {
      delete resolvers.Query[key];
    }
  }
  if (resolvers.Query && Object.keys(resolvers.Query).length > 0) {
    for (const [key, value] of Object.entries(resolvers.Query)) {
      if (!graphqlResolvers.Query) {
        graphqlResolvers.Query = {};
      }
      graphqlResolvers.Query[key] = rootGraphqlResolver(
        value
      );
    }
  }
  if (resolvers.Mutation && Object.keys(resolvers.Mutation).length > 0) {
    if (!graphqlResolvers.Mutation) {
      graphqlResolvers.Mutation = {};
    }
    for (const [key, value] of Object.entries(resolvers.Mutation)) {
      graphqlResolvers.Mutation[key] = rootGraphqlResolver(
        value
      );
    }
  }
  if (resolvers.Subscription && Object.keys(resolvers.Subscription).length > 0) {
    if (!graphqlResolvers.Subscription) {
      graphqlResolvers.Subscription = {};
    }
    for (const [key, value] of Object.entries(resolvers.Subscription)) {
      graphqlResolvers.Subscription[key] = {
        subscribe: rootGraphqlResolver(value),
        resolve: (payload) => payload
      };
    }
  }
  if (!graphqlResolvers.Query) {
    throw new Error(`At least one 'Query' resolver must be provided.

Example:

export const graphql = {
  Query: {
    // Define at least one query resolver here
    hello: () => 'world'
  }
}
`);
  }
  for (const key of Object.keys(resolvers)) {
    if (key !== "Query" && key !== "Mutation" && key !== "Subscription") {
      graphqlResolvers[key] = resolvers[key];
    }
  }
  return graphqlResolvers;
};
var ServiceError = class extends GraphQLError {
  extensions;
  constructor(message, extensions, error) {
    super(message, {
      originalError: error
    });
    this.extensions = extensions;
    this.cause = error;
  }
};

// src/plugins/use-auth/use-auth.ts
import { promises as fs } from "fs";
import { deleteCookie, getCookie, setCookie } from "hono/cookie";
import { HTTPException } from "hono/http-exception";
import * as openid from "openid-client";
import path from "path";

// src/plugins/use-auth/import-private-key.ts
import * as crypto2 from "crypto";
function str2ab(str) {
  const buf = new ArrayBuffer(str.length);
  const bufView = new Uint8Array(buf);
  for (let i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}
var convertPKCS1ToPKCS8 = (pkcs1) => {
  const key = crypto2.createPrivateKey(pkcs1);
  return key.export({
    type: "pkcs8",
    format: "pem"
  });
};
function importPKCS8PrivateKey(pem) {
  const pemHeader = "-----BEGIN PRIVATE KEY-----";
  const pemFooter = "-----END PRIVATE KEY-----";
  const pemContents = pem.substring(
    pemHeader.length,
    pem.length - pemFooter.length - 1
  );
  const binaryDerString = atob(pemContents);
  const binaryDer = str2ab(binaryDerString);
  return crypto2.subtle.importKey(
    "pkcs8",
    binaryDer,
    {
      name: "RSASSA-PKCS1-v1_5",
      hash: "SHA-256"
    },
    true,
    ["sign"]
  );
}
var importPrivateKey = async (pkcs1Pem) => {
  const pkcs8Pem = convertPKCS1ToPKCS8(pkcs1Pem);
  return await importPKCS8PrivateKey(pkcs8Pem);
};

// src/plugins/use-auth/use-auth.ts
var loadAuthKey = async (keyPath) => {
  const authKeyFilePath = path.join(process.cwd(), keyPath);
  const env3 = getContext().env;
  if (env3.AUTH_KEY) {
    try {
      return JSON.parse(env3.AUTH_KEY);
    } catch (error) {
      throw new Error(
        "Error while reading AUTH_KEY. Make sure it is valid JSON"
      );
    }
  }
  try {
    const ketFileContent = await fs.readFile(authKeyFilePath, "utf-8");
    try {
      return JSON.parse(ketFileContent);
    } catch (error) {
      throw new Error(
        "Error while reading key file. Make sure it is valid JSON"
      );
    }
  } catch (error) {
    throw new Error("Error while reading key file. Make sure it exists");
  }
};
var openidConfigCache;
var bootstrapAuth = async (issuer, keyPath) => {
  if (!openidConfigCache) {
    const authKey = await loadAuthKey(keyPath);
    openidConfigCache = await openid.discovery(
      new URL(issuer),
      authKey.clientId,
      void 0,
      openid.PrivateKeyJwt({
        key: await importPrivateKey(authKey.key),
        kid: authKey.keyId
      })
    );
  }
  return openidConfigCache;
};
var PylonAuthException = class extends HTTPException {
  // Same constructor as HTTPException
  constructor(...args) {
    args[1] = {
      ...args[1],
      message: `PylonAuthException: ${args[1]?.message}`
    };
    super(...args);
  }
};
function useAuth(args) {
  const { issuer, endpoint = "/auth", keyPath = "key.json" } = args;
  const loginPath = `${endpoint}/login`;
  const logoutPath = `${endpoint}/logout`;
  const callbackPath = `${endpoint}/callback`;
  return {
    middleware: async (ctx, next) => {
      const openidConfig = await bootstrapAuth(issuer, keyPath);
      ctx.set("auth", { openidConfig });
      const authCookieToken = getCookie(ctx, "pylon-auth");
      const authHeader = ctx.req.header("Authorization");
      const authQueryToken = ctx.req.query("token");
      if (authCookieToken || authHeader || authQueryToken) {
        let token;
        if (authHeader) {
          const [type, value] = authHeader.split(" ");
          if (type === "Bearer") {
            token = value;
          }
        } else if (authQueryToken) {
          token = authQueryToken;
        } else if (authCookieToken) {
          token = authCookieToken;
        }
        if (!token) {
          throw new PylonAuthException(401, {
            message: "Invalid token"
          });
        }
        const introspection = await openid.tokenIntrospection(
          openidConfig,
          token,
          {
            scope: "openid email profile"
          }
        );
        if (!introspection.active) {
          throw new PylonAuthException(401, {
            message: "Token is not active"
          });
        }
        if (!introspection.sub) {
          throw new PylonAuthException(401, {
            message: "Token is missing subject"
          });
        }
        const userInfo = await openid.fetchUserInfo(
          openidConfig,
          token,
          introspection.sub
        );
        const roles = Object.keys(
          introspection["urn:zitadel:iam:org:projects:roles"]?.valueOf() || {}
        );
        ctx.set("auth", {
          user: {
            ...userInfo,
            roles
          },
          openidConfig
        });
        return next();
      }
    },
    setup(app2) {
      app2.get(loginPath, async (ctx) => {
        const openidConfig = ctx.get("auth").openidConfig;
        const codeVerifier = openid.randomPKCECodeVerifier();
        const codeChallenge = await openid.calculatePKCECodeChallenge(
          codeVerifier
        );
        setCookie(ctx, "pylon_code_verifier", codeVerifier, {
          httpOnly: true,
          maxAge: 300
          // 5 minutes
        });
        let scope = "openid profile email urn:zitadel:iam:user:resourceowner urn:zitadel:iam:org:projects:roles";
        const parameters = {
          scope,
          code_challenge: codeChallenge,
          code_challenge_method: "S256",
          redirect_uri: new URL(ctx.req.url).origin + "/auth/callback",
          state: openid.randomState()
        };
        const authorizationUrl = openid.buildAuthorizationUrl(
          openidConfig,
          parameters
        );
        return ctx.redirect(authorizationUrl);
      });
      app2.get(logoutPath, async (ctx) => {
        deleteCookie(ctx, "pylon-auth");
        return ctx.redirect("/");
      });
      app2.get(callbackPath, async (ctx) => {
        const openidConfig = ctx.get("auth").openidConfig;
        const params = ctx.req.query();
        const code = params.code;
        const state = params.state;
        if (!code || !state) {
          throw new PylonAuthException(400, {
            message: "Missing authorization code or state"
          });
        }
        const codeVerifier = getCookie(ctx, "pylon_code_verifier");
        if (!codeVerifier) {
          throw new PylonAuthException(400, {
            message: "Missing code verifier"
          });
        }
        try {
          const cbUrl = new URL(ctx.req.url);
          let tokenSet = await openid.authorizationCodeGrant(
            openidConfig,
            cbUrl,
            {
              pkceCodeVerifier: codeVerifier,
              expectedState: state
            },
            cbUrl.searchParams
          );
          setCookie(ctx, `pylon-auth`, tokenSet.access_token, {
            httpOnly: true,
            maxAge: tokenSet.expires_in || 3600
            // Default to 1 hour if not specified
          });
          return ctx.redirect("/");
        } catch (error) {
          console.error("Error during token exchange:", error);
          return ctx.text("Authentication failed!", 500);
        }
      });
    }
  };
}

// src/plugins/use-auth/auth-require.ts
import { env as env2 } from "hono/adapter";
import { HTTPException as HTTPException2 } from "hono/http-exception";

// src/create-decorator.ts
function createDecorator(callback) {
  function MyDecorator(arg1, propertyKey, descriptor) {
    if (descriptor) {
      const originalMethod = descriptor.value;
      descriptor.value = async function(...args) {
        await callback(...args);
        return originalMethod.apply(this, args);
      };
      return descriptor;
    } else {
      if (!descriptor) {
        if (propertyKey === void 0) {
          const originalFunction = arg1;
          return async function(...args) {
            await callback(...args);
            return originalFunction(...args);
          };
        }
        let value = arg1[propertyKey];
        Object.defineProperty(arg1, propertyKey, {
          get: function() {
            return async function(...args) {
              await callback(...args);
              if (typeof value === "function") {
                return value(...args);
              }
              return value;
            };
          },
          set: function(newValue) {
            value = newValue;
          },
          enumerable: true,
          configurable: true
        });
        return;
      }
    }
  }
  return MyDecorator;
}

// src/plugins/use-auth/auth-require.ts
var authMiddleware = (checks = {}) => {
  const middleware = async (ctx, next) => {
    const AUTH_PROJECT_ID = env2(ctx).AUTH_PROJECT_ID;
    const auth = ctx.get("auth");
    if (!auth) {
      throw new HTTPException2(401, {
        message: "Authentication required"
      });
    }
    if (checks.roles && auth.user) {
      const roles = auth.user.roles;
      const hasRole = checks.roles.some((role) => {
        return roles.includes(role) || roles.includes(`${AUTH_PROJECT_ID}:${role}`);
      });
      if (!hasRole) {
        const resError = new Response("Forbidden", {
          status: 403,
          statusText: "Forbidden",
          headers: {
            "Missing-Roles": checks.roles.join(","),
            "Obtained-Roles": roles.join(",")
          }
        });
        throw new HTTPException2(resError.status, {
          res: resError
        });
      }
    }
    return next();
  };
  return middleware;
};
function requireAuth(checks) {
  const checkAuth = async (c) => {
    const ctx = await c;
    try {
      await authMiddleware(checks)(ctx, async () => {
      });
    } catch (e) {
      if (e instanceof HTTPException2) {
        if (e.status === 401) {
          throw new ServiceError(e.message, {
            statusCode: 401,
            code: "AUTH_REQUIRED"
          });
        } else if (e.status === 403) {
          const res = e.getResponse();
          throw new ServiceError(res.statusText, {
            statusCode: res.status,
            code: "AUTHORIZATION_REQUIRED",
            details: {
              missingRoles: res.headers.get("Missing-Roles")?.split(","),
              obtainedRoles: res.headers.get("Obtained-Roles")?.split(",")
            }
          });
        } else {
          throw e;
        }
      }
      throw e;
    }
  };
  return createDecorator(async () => {
    const ctx = getContext();
    await checkAuth(ctx);
  });
}

// src/app/index.ts
import { Hono } from "hono";
import { logger } from "hono/logger";
import { sentry } from "@hono/sentry";
import { except } from "hono/combine";
var app = new Hono();
app.use("*", sentry());
app.use("*", async (c, next) => {
  return new Promise((resolve, reject) => {
    asyncContext.run(c, async () => {
      try {
        resolve(await next());
      } catch (error) {
        reject(error);
      }
    });
  });
});
app.use("*", except(["/__pylon/*"], logger()));
app.use((c, next) => {
  c.req.id = crypto.randomUUID();
  return next();
});
var pluginsMiddleware = [];
var pluginsMiddlewareLoader = async (c, next) => {
  for (const middleware of pluginsMiddleware) {
    const response = await middleware(c, async () => {
    });
    if (response) {
      return response;
    }
  }
  return next();
};
app.use(pluginsMiddlewareLoader);

// src/app/pylon-handler.ts
import { createSchema, createYoga } from "graphql-yoga";
import { GraphQLScalarType, Kind as Kind2 } from "graphql";
import {
  DateTimeISOResolver,
  GraphQLVoid,
  JSONObjectResolver,
  JSONResolver
} from "graphql-scalars";

// src/plugins/use-sentry.ts
import { Kind, print } from "graphql";
import {
  getDocumentString,
  handleStreamOrSingleExecutionResult,
  isOriginalGraphQLError
} from "@envelop/core";
import * as Sentry2 from "@sentry/node";
var defaultSkipError = isOriginalGraphQLError;
var useSentry = (options = {}) => {
  function pick(key, defaultValue) {
    return options[key] ?? defaultValue;
  }
  const startTransaction = pick("startTransaction", true);
  const includeRawResult = pick("includeRawResult", false);
  const includeExecuteVariables = pick("includeExecuteVariables", false);
  const renameTransaction = pick("renameTransaction", false);
  const skipOperation = pick("skip", () => false);
  const skipError = pick("skipError", defaultSkipError);
  const eventIdKey = options.eventIdKey === null ? null : "sentryEventId";
  function addEventId(err, eventId) {
    if (eventIdKey !== null && eventId !== null) {
      err.extensions[eventIdKey] = eventId;
    }
    return err;
  }
  return {
    onExecute({ args }) {
      if (skipOperation(args)) {
        return;
      }
      const rootOperation = args.document.definitions.find(
        (o) => o.kind === Kind.OPERATION_DEFINITION
      );
      const operationType = rootOperation.operation;
      const document = getDocumentString(args.document, print);
      const opName = args.operationName || rootOperation.name?.value || "Anonymous Operation";
      const addedTags = options.appendTags && options.appendTags(args) || {};
      const traceparentData = options.traceparentData && options.traceparentData(args) || {};
      const transactionName = options.transactionName ? options.transactionName(args) : opName;
      const op = options.operationName ? options.operationName(args) : "execute";
      const tags = {
        operationName: opName,
        operation: operationType,
        ...addedTags
      };
      if (options.configureScope) {
        options.configureScope(args, Sentry2.getCurrentScope());
      }
      return {
        onExecuteDone(payload) {
          const handleResult = ({
            result,
            setResult
          }) => {
            Sentry2.startSpanManual(
              {
                op,
                name: opName,
                attributes: tags
              },
              (span) => {
                if (renameTransaction) {
                  span.updateName(transactionName);
                }
                span.setAttribute("document", document);
                if (includeRawResult) {
                  span.setAttribute("result", JSON.stringify(result));
                }
                if (result.errors && result.errors.length > 0) {
                  Sentry2.withScope((scope) => {
                    scope.setTransactionName(opName);
                    scope.setTag("operation", operationType);
                    scope.setTag("operationName", opName);
                    scope.setExtra("document", document);
                    scope.setTags(addedTags || {});
                    if (includeRawResult) {
                      scope.setExtra("result", result);
                    }
                    if (includeExecuteVariables) {
                      scope.setExtra("variables", args.variableValues);
                    }
                    const errors = result.errors?.map((err) => {
                      if (skipError(err) === true) {
                        return err;
                      }
                      const errorPath = (err.path ?? []).map(
                        (v) => typeof v === "number" ? "$index" : v
                      ).join(" > ");
                      if (errorPath) {
                        scope.addBreadcrumb({
                          category: "execution-path",
                          message: errorPath,
                          level: "debug"
                        });
                      }
                      const eventId = Sentry2.captureException(
                        err.originalError,
                        {
                          fingerprint: [
                            "graphql",
                            errorPath,
                            opName,
                            operationType
                          ],
                          contexts: {
                            GraphQL: {
                              operationName: opName,
                              operationType,
                              variables: args.variableValues
                            }
                          }
                        }
                      );
                      return addEventId(err, eventId);
                    });
                    setResult({
                      ...result,
                      errors
                    });
                  });
                }
                span.end();
              }
            );
          };
          return handleStreamOrSingleExecutionResult(payload, handleResult);
        }
      };
    }
  };
};

// src/app/pylon-handler.ts
import { readFileSync } from "fs";
import path2 from "path";

// src/plugins/use-viewer.ts
import { html } from "hono/html";
function useViewer() {
  return {
    setup: (app2) => {
      app2.get("/viewer", async (c) => {
        return c.html(
          await html`
            <!DOCTYPE html>
            <html>
              <head>
                <title>Pylon Viewer</title>
                <script src="https://cdn.jsdelivr.net/npm/react@16/umd/react.production.min.js"></script>
                <script src="https://cdn.jsdelivr.net/npm/react-dom@16/umd/react-dom.production.min.js"></script>

                <link
                  rel="stylesheet"
                  href="https://cdn.jsdelivr.net/npm/graphql-voyager/dist/voyager.css"
                />
                <style>
                    body {
                      padding: 0;
                      margin: 0;
                      width: 100%;
                      height: 100vh;
                      overflow: hidden;
                    }

                    #voyager {
                      height: 100%;
                      position: relative;
                    }
                  }
                </style>
                <script src="https://cdn.jsdelivr.net/npm/graphql-voyager/dist/voyager.min.js"></script>
              </head>
              <body>
                <div id="voyager">Loading...</div>
                <script>
                  function introspectionProvider(introspectionQuery) {
                    // ... do a call to server using introspectionQuery provided
                    // or just return pre-fetched introspection

                    // Endpoint is current path instead of root/graphql
                    const endpoint = window.location.pathname.replace(
                      '/viewer',
                      '/graphql'
                    )

                    return fetch(endpoint, {
                      method: 'post',
                      headers: {
                        'Content-Type': 'application/json'
                      },
                      body: JSON.stringify({query: introspectionQuery})
                    }).then(response => response.json())
                  }

                  // Render <Voyager />
                  GraphQLVoyager.init(document.getElementById('voyager'), {
                    introspection: introspectionProvider
                  })
                </script>
              </body>
            </html>
          `
        );
      });
    }
  };
}

// src/plugins/use-unhandled-route.ts
import { html as html2 } from "hono/html";

// package.json
var version = "3.0.0-canary-20251114081320.eebcc06b20e2b45fd9717fa0911912f568b21784";

// src/plugins/use-unhandled-route.ts
function useUnhandledRoute() {
  return {
    setup: (app2) => {
      app2.notFound((c) => {
        return c.html(
          html2`<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Welcome to Pylon</title>
<link
rel="icon"
href="https://pylon.cronit.io/favicon/favicon.ico"
/>
<style>
body,
html {
padding: 0;
margin: 0;
height: 100%;
font-family:
  'Inter',
  -apple-system,
  BlinkMacSystemFont,
  'Segoe UI',
  'Roboto',
  'Oxygen',
  'Ubuntu',
  'Cantarell',
  'Fira Sans',
  'Droid Sans',
  'Helvetica Neue',
  sans-serif;
color: white;
background-color: black;
}

main > section.hero {
display: flex;
height: 90vh;
justify-content: center;
align-items: center;
flex-direction: column;
}

.logo {
display: flex;
align-items: center;
}

.logo-svg {
width: 100%
}

.buttons {
margin-top: 24px;
}

h1 {
font-size: 80px;
}

h2 {
color: #888;
max-width: 50%;
margin-top: 0;
text-align: center;
}

a {
color: #fff;
text-decoration: none;
margin-left: 10px;
margin-right: 10px;
font-weight: bold;
transition: color 0.3s ease;
padding: 4px;
overflow: visible;
}

a.graphiql:hover {
color: rgba(255, 0, 255, 0.7);
}
a.docs:hover {
color: rgba(28, 200, 238, 0.7);
}
a.tutorial:hover {
color: rgba(125, 85, 245, 0.7);
}
svg {
margin-right: 24px;
}

.not-what-your-looking-for {
margin-top: 5vh;
}

.not-what-your-looking-for > * {
margin-left: auto;
margin-right: auto;
}

.not-what-your-looking-for > p {
text-align: center;
}

.not-what-your-looking-for > h2 {
color: #464646;
}

.not-what-your-looking-for > p {
max-width: 600px;
line-height: 1.3em;
}

.not-what-your-looking-for > pre {
max-width: 300px;
}
</style>
</head>
<body id="body">
<main>
<section class="hero">
<div class="logo">
  <div>
    <svg class="logo-svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" zoomAndPan="magnify" viewBox="0 0 286.5 121.500001" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><g></g><clipPath id="38f6fcde47"><path d="M 0.339844 42 L 10 42 L 10 79 L 0.339844 79 Z M 0.339844 42 " clip-rule="nonzero"></path></clipPath><clipPath id="af000f7256"><path d="M 64 23.925781 L 72.789062 23.925781 L 72.789062 96.378906 L 64 96.378906 Z M 64 23.925781 " clip-rule="nonzero"></path></clipPath></defs><g fill="currentColor" fill-opacity="1"><g transform="translate(107.11969, 78.49768)"><g><path d="M 10.078125 -25.046875 C 11.109375 -26.398438 12.507812 -27.535156 14.28125 -28.453125 C 16.0625 -29.378906 18.070312 -29.84375 20.3125 -29.84375 C 22.863281 -29.84375 25.195312 -29.210938 27.3125 -27.953125 C 29.425781 -26.691406 31.085938 -24.921875 32.296875 -22.640625 C 33.503906 -20.367188 34.109375 -17.757812 34.109375 -14.8125 C 34.109375 -11.863281 33.503906 -9.222656 32.296875 -6.890625 C 31.085938 -4.566406 29.425781 -2.753906 27.3125 -1.453125 C 25.195312 -0.160156 22.863281 0.484375 20.3125 0.484375 C 18.070312 0.484375 16.078125 0.03125 14.328125 -0.875 C 12.585938 -1.78125 11.171875 -2.910156 10.078125 -4.265625 L 10.078125 13.96875 L 4 13.96875 L 4 -29.359375 L 10.078125 -29.359375 Z M 27.921875 -14.8125 C 27.921875 -16.84375 27.503906 -18.59375 26.671875 -20.0625 C 25.835938 -21.539062 24.734375 -22.660156 23.359375 -23.421875 C 21.992188 -24.179688 20.53125 -24.5625 18.96875 -24.5625 C 17.445312 -24.5625 16 -24.171875 14.625 -23.390625 C 13.257812 -22.609375 12.160156 -21.472656 11.328125 -19.984375 C 10.492188 -18.492188 10.078125 -16.734375 10.078125 -14.703125 C 10.078125 -12.679688 10.492188 -10.914062 11.328125 -9.40625 C 12.160156 -7.894531 13.257812 -6.75 14.625 -5.96875 C 16 -5.1875 17.445312 -4.796875 18.96875 -4.796875 C 20.53125 -4.796875 21.992188 -5.191406 23.359375 -5.984375 C 24.734375 -6.785156 25.835938 -7.953125 26.671875 -9.484375 C 27.503906 -11.015625 27.921875 -12.789062 27.921875 -14.8125 Z M 27.921875 -14.8125 "></path></g></g></g><g fill="currentColor" fill-opacity="1"><g transform="translate(143.259256, 78.49768)"><g><path d="M 30.4375 -29.359375 L 12.421875 13.796875 L 6.125 13.796875 L 12.09375 -0.484375 L 0.53125 -29.359375 L 7.296875 -29.359375 L 15.5625 -6.984375 L 24.140625 -29.359375 Z M 30.4375 -29.359375 "></path></g></g></g><g fill="currentColor" fill-opacity="1"><g transform="translate(174.281707, 78.49768)"><g><path d="M 10.078125 -39.4375 L 10.078125 0 L 4 0 L 4 -39.4375 Z M 10.078125 -39.4375 "></path></g></g></g><g fill="currentColor" fill-opacity="1"><g transform="translate(188.353752, 78.49768)"><g><path d="M 16.734375 0.484375 C 13.960938 0.484375 11.457031 -0.144531 9.21875 -1.40625 C 6.976562 -2.664062 5.21875 -4.441406 3.9375 -6.734375 C 2.664062 -9.035156 2.03125 -11.691406 2.03125 -14.703125 C 2.03125 -17.691406 2.6875 -20.335938 4 -22.640625 C 5.3125 -24.953125 7.101562 -26.726562 9.375 -27.96875 C 11.65625 -29.21875 14.195312 -29.84375 17 -29.84375 C 19.8125 -29.84375 22.351562 -29.21875 24.625 -27.96875 C 26.894531 -26.726562 28.6875 -24.953125 30 -22.640625 C 31.320312 -20.335938 31.984375 -17.691406 31.984375 -14.703125 C 31.984375 -11.722656 31.304688 -9.078125 29.953125 -6.765625 C 28.597656 -4.453125 26.757812 -2.664062 24.4375 -1.40625 C 22.113281 -0.144531 19.546875 0.484375 16.734375 0.484375 Z M 16.734375 -4.796875 C 18.296875 -4.796875 19.757812 -5.164062 21.125 -5.90625 C 22.5 -6.65625 23.613281 -7.773438 24.46875 -9.265625 C 25.320312 -10.765625 25.75 -12.578125 25.75 -14.703125 C 25.75 -16.835938 25.335938 -18.640625 24.515625 -20.109375 C 23.703125 -21.585938 22.617188 -22.695312 21.265625 -23.4375 C 19.910156 -24.1875 18.453125 -24.5625 16.890625 -24.5625 C 15.328125 -24.5625 13.878906 -24.1875 12.546875 -23.4375 C 11.210938 -22.695312 10.15625 -21.585938 9.375 -20.109375 C 8.59375 -18.640625 8.203125 -16.835938 8.203125 -14.703125 C 8.203125 -11.546875 9.007812 -9.101562 10.625 -7.375 C 12.25 -5.65625 14.285156 -4.796875 16.734375 -4.796875 Z M 16.734375 -4.796875 "></path></g></g></g><g fill="currentColor" fill-opacity="1"><g transform="translate(222.361196, 78.49768)"><g><path d="M 18.8125 -29.84375 C 21.125 -29.84375 23.191406 -29.363281 25.015625 -28.40625 C 26.847656 -27.445312 28.28125 -26.023438 29.3125 -24.140625 C 30.34375 -22.253906 30.859375 -19.984375 30.859375 -17.328125 L 30.859375 0 L 24.84375 0 L 24.84375 -16.421875 C 24.84375 -19.046875 24.179688 -21.054688 22.859375 -22.453125 C 21.546875 -23.859375 19.753906 -24.5625 17.484375 -24.5625 C 15.210938 -24.5625 13.410156 -23.859375 12.078125 -22.453125 C 10.742188 -21.054688 10.078125 -19.046875 10.078125 -16.421875 L 10.078125 0 L 4 0 L 4 -29.359375 L 10.078125 -29.359375 L 10.078125 -26.015625 C 11.066406 -27.222656 12.332031 -28.160156 13.875 -28.828125 C 15.425781 -29.503906 17.070312 -29.84375 18.8125 -29.84375 Z M 18.8125 -29.84375 "></path></g></g></g><path fill="currentColor" d="M 53.359375 31.652344 L 53.359375 88.6875 L 62.410156 90.859375 L 62.410156 29.484375 Z M 53.359375 31.652344 " fill-opacity="1" fill-rule="nonzero"></path><g clip-path="url(#38f6fcde47)"><path fill="currentColor" d="M 0.339844 47.433594 L 0.339844 72.910156 C 0.339844 73.34375 0.410156 73.769531 0.554688 74.179688 C 0.699219 74.59375 0.90625 74.96875 1.175781 75.3125 C 1.445312 75.65625 1.765625 75.945312 2.132812 76.179688 C 2.503906 76.414062 2.898438 76.582031 3.324219 76.683594 L 9.390625 78.140625 L 9.390625 42.195312 L 3.3125 43.660156 C 2.890625 43.761719 2.492188 43.929688 2.125 44.164062 C 1.761719 44.402344 1.441406 44.6875 1.171875 45.03125 C 0.902344 45.375 0.695312 45.75 0.554688 46.164062 C 0.410156 46.574219 0.339844 46.996094 0.339844 47.433594 Z M 0.339844 47.433594 " fill-opacity="1" fill-rule="nonzero"></path></g><g clip-path="url(#af000f7256)"><path fill="currentColor" d="M 64.996094 95.085938 L 64.996094 25.253906 C 64.996094 25.082031 65.027344 24.917969 65.09375 24.761719 C 65.160156 24.601562 65.253906 24.460938 65.375 24.339844 C 65.496094 24.21875 65.636719 24.125 65.792969 24.0625 C 65.953125 23.996094 66.117188 23.960938 66.289062 23.960938 L 71.460938 23.960938 C 71.632812 23.960938 71.796875 23.996094 71.957031 24.0625 C 72.113281 24.125 72.253906 24.21875 72.375 24.339844 C 72.496094 24.460938 72.589844 24.601562 72.65625 24.761719 C 72.722656 24.917969 72.753906 25.082031 72.753906 25.253906 L 72.753906 95.085938 C 72.753906 95.257812 72.722656 95.421875 72.65625 95.582031 C 72.589844 95.738281 72.496094 95.878906 72.375 96 C 72.253906 96.121094 72.113281 96.214844 71.957031 96.28125 C 71.796875 96.347656 71.632812 96.378906 71.460938 96.378906 L 66.289062 96.378906 C 66.117188 96.378906 65.953125 96.347656 65.792969 96.28125 C 65.636719 96.214844 65.496094 96.121094 65.375 96 C 65.253906 95.878906 65.160156 95.738281 65.09375 95.582031 C 65.027344 95.421875 64.996094 95.257812 64.996094 95.085938 Z M 64.996094 95.085938 " fill-opacity="1" fill-rule="nonzero"></path></g><path fill="currentColor" d="M 22.320312 81.238281 L 22.320312 39.101562 L 11.976562 41.585938 L 11.976562 78.757812 Z M 22.320312 81.238281 " fill-opacity="1" fill-rule="nonzero"></path><path fill="currentColor" d="M 50.769531 88.066406 L 50.769531 32.277344 L 37.839844 35.378906 L 37.839844 84.960938 Z M 50.769531 88.066406 " fill-opacity="1" fill-rule="nonzero"></path><path fill="currentColor" d="M 24.90625 81.863281 L 35.253906 84.34375 L 35.253906 35.996094 L 24.90625 38.480469 Z M 24.90625 81.863281 " fill-opacity="1" fill-rule="nonzero"></path></svg>
  </div>
  <p>Version: ${version}</p>
</div>
<h2>Enables TypeScript developers to easily build GraphQL APIs</h2>
<div class="buttons">
  <a href="https://pylon.cronit.io/docs" class="docs"
    >Read the Docs</add
  >
  <a href="/graphql" class="graphiql">Visit GraphiQL</a>
  <a href="/viewer" class="graphiql">Visit Viewer</a>
</div>
</section>
<section class="not-what-your-looking-for">
<h2>Not the page you are looking for? 👀</h2>
<p>
  This page is shown be default whenever a 404 is hit.<br />You can disable this by behavior
  via the <code>landingPage</code> option in the Pylon config. Edit the <code>src/index.ts</code> file
  and add the following code:
</p>
<pre>
  <code>
export const config: PylonConfig = {
  landingPage: false
}
  </code>
</pre>

<p>
  When you define a route, this page will no longer be shown. For example, the following code
will show a "Hello, world!" message at the root of your app:
</p>
<pre>
  <code>
import {app} from '@getcronit/pylon'

app.get("/", c => {
  return c.text("Hello, world!")
})
  </code>
</pre>
</section>
</main>
</body>
</html>`,
          404
        );
      });
    }
  };
}

// src/app/pylon-handler.ts
import { useDisableIntrospection } from "@graphql-yoga/plugin-disable-introspection";
var resolveLazyObject = (obj) => {
  return typeof obj === "function" ? obj() : obj;
};
var loadPluginsMiddleware = async (plugins) => {
  for (const plugin of plugins) {
    await plugin.setup?.(app);
    if (plugin.middleware) {
      pluginsMiddleware.push(plugin.middleware);
    }
  }
};
var executeConfig = async (config, args) => {
  const plugins = [useSentry(), useViewer(), ...config?.plugins || []];
  if (config?.landingPage ?? true) {
    plugins.push(useUnhandledRoute());
  }
  if (config?.graphiql === false) {
    plugins.push(useDisableIntrospection());
  }
  const pluginsStrategy = args?.pluginsStrategy || "first";
  await loadPluginsMiddleware(
    plugins.filter((p) => {
      if (!p.strategy) {
        p.strategy = "first";
      }
      return p.strategy === pluginsStrategy;
    })
  );
  config.plugins = plugins;
  app.config = config;
};
var handler = (options) => {
  let {
    typeDefs,
    resolvers,
    graphql: graphql$
  } = options;
  const graphql = resolveLazyObject(graphql$);
  const config = app.config;
  if (!typeDefs) {
    const schemaPath = path2.join(process.cwd(), ".pylon", "schema.graphql");
    if (schemaPath) {
      typeDefs = readFileSync(schemaPath, "utf-8");
    }
  }
  if (!typeDefs) {
    throw new Error("No schema provided.");
  }
  if (!resolvers) {
    const resolversPath = path2.join(process.cwd(), ".pylon", "resolvers.js");
    if (resolversPath) {
      resolvers = __require(resolversPath).resolvers;
    }
  }
  const graphqlResolvers = resolversToGraphQLResolvers(graphql);
  const schema = createSchema({
    typeDefs,
    resolvers: {
      ...graphqlResolvers,
      ...resolvers,
      // Transforms a date object to a timestamp
      Date: DateTimeISOResolver,
      JSON: JSONResolver,
      Object: JSONObjectResolver,
      Void: GraphQLVoid,
      Number: new GraphQLScalarType({
        name: "Number",
        description: "Custom scalar that handles both integers and floats",
        // Parsing input from query variables
        parseValue(value) {
          if (typeof value !== "number") {
            throw new TypeError(`Value is not a number: ${value}`);
          }
          return value;
        },
        // Validation when sending from client (input literals)
        parseLiteral(ast) {
          if (ast.kind === Kind2.INT || ast.kind === Kind2.FLOAT) {
            return parseFloat(ast.value);
          }
          throw new TypeError(
            `Value is not a valid number or float: ${"value" in ast ? ast.value : ast}`
          );
        },
        // Serialize output to be sent to the client
        serialize(value) {
          if (typeof value !== "number") {
            throw new TypeError(`Value is not a number: ${value}`);
          }
          return value;
        }
      })
    }
  });
  const yoga = createYoga({
    graphqlEndpoint: "/graphql",
    ...config,
    landingPage: false,
    graphiql: config?.graphiql !== false ? (req) => {
      return {
        shouldPersistHeaders: true,
        title: "Pylon Playground",
        defaultQuery: `# Welcome to the Pylon Playground!`
      };
    } : false,
    schema
  });
  const handler2 = async (c, next) => {
    let executionContext = {};
    try {
      executionContext = c.executionCtx;
    } catch (e) {
    }
    const response = await yoga.fetch(c.req.raw, c.env, executionContext);
    if (response.status === 404) {
      return next();
    }
    return c.newResponse(response.body, response);
  };
  return handler2;
};

// src/get-env.ts
function getEnv() {
  const start = Date.now();
  const skipTracing = arguments[0] === true;
  try {
    const context = asyncContext.getStore();
    const ctx = context.env || process.env || {};
    ctx.NODE_ENV = ctx.NODE_ENV || process.env.NODE_ENV || "development";
    return ctx;
  } catch {
    return process.env;
  } finally {
    if (!skipTracing) {
    }
  }
}

// src/index.ts
import { createPubSub } from "graphql-yoga";

// src/plugins/use-pages/setup/index.tsx
import fs2 from "fs";
import path3 from "path";
import reactServer from "react-dom/server";
import { trimTrailingSlash } from "hono/trailing-slash";
import {
  createStaticHandler,
  createStaticRouter,
  StaticRouterProvider
} from "react-router";
import { PassThrough, Readable as Readable2 } from "stream";

// src/components/global-error-page.tsx
import { useEffect } from "react";

// src/lib/utils.ts
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
function cn(...inputs) {
  return twMerge(clsx(inputs));
}

// src/components/logo.tsx
import { jsx, jsxs } from "react/jsx-runtime";
var Logo = (props) => {
  return /* @__PURE__ */ jsxs(
    "svg",
    {
      className: cn("h-12 w-auto", props.className),
      xmlns: "http://www.w3.org/2000/svg",
      xmlnsXlink: "http://www.w3.org/1999/xlink",
      zoomAndPan: "magnify",
      viewBox: "0 0 286.5 121.500001",
      preserveAspectRatio: "xMidYMid meet",
      version: "1.0",
      children: [
        /* @__PURE__ */ jsxs("defs", { children: [
          /* @__PURE__ */ jsx("g", {}),
          /* @__PURE__ */ jsx("clipPath", { id: "38f6fcde47", children: /* @__PURE__ */ jsx(
            "path",
            {
              d: "M 0.339844 42 L 10 42 L 10 79 L 0.339844 79 Z M 0.339844 42 ",
              clipRule: "nonzero"
            }
          ) }),
          /* @__PURE__ */ jsx("clipPath", { id: "af000f7256", children: /* @__PURE__ */ jsx(
            "path",
            {
              d: "M 64 23.925781 L 72.789062 23.925781 L 72.789062 96.378906 L 64 96.378906 Z M 64 23.925781 ",
              clipRule: "nonzero"
            }
          ) })
        ] }),
        /* @__PURE__ */ jsx("g", { fill: "currentColor", fillOpacity: "1", children: /* @__PURE__ */ jsx("g", { transform: "translate(107.11969, 78.49768)", children: /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx("path", { d: "M 10.078125 -25.046875 C 11.109375 -26.398438 12.507812 -27.535156 14.28125 -28.453125 C 16.0625 -29.378906 18.070312 -29.84375 20.3125 -29.84375 C 22.863281 -29.84375 25.195312 -29.210938 27.3125 -27.953125 C 29.425781 -26.691406 31.085938 -24.921875 32.296875 -22.640625 C 33.503906 -20.367188 34.109375 -17.757812 34.109375 -14.8125 C 34.109375 -11.863281 33.503906 -9.222656 32.296875 -6.890625 C 31.085938 -4.566406 29.425781 -2.753906 27.3125 -1.453125 C 25.195312 -0.160156 22.863281 0.484375 20.3125 0.484375 C 18.070312 0.484375 16.078125 0.03125 14.328125 -0.875 C 12.585938 -1.78125 11.171875 -2.910156 10.078125 -4.265625 L 10.078125 13.96875 L 4 13.96875 L 4 -29.359375 L 10.078125 -29.359375 Z M 27.921875 -14.8125 C 27.921875 -16.84375 27.503906 -18.59375 26.671875 -20.0625 C 25.835938 -21.539062 24.734375 -22.660156 23.359375 -23.421875 C 21.992188 -24.179688 20.53125 -24.5625 18.96875 -24.5625 C 17.445312 -24.5625 16 -24.171875 14.625 -23.390625 C 13.257812 -22.609375 12.160156 -21.472656 11.328125 -19.984375 C 10.492188 -18.492188 10.078125 -16.734375 10.078125 -14.703125 C 10.078125 -12.679688 10.492188 -10.914062 11.328125 -9.40625 C 12.160156 -7.894531 13.257812 -6.75 14.625 -5.96875 C 16 -5.1875 17.445312 -4.796875 18.96875 -4.796875 C 20.53125 -4.796875 21.992188 -5.191406 23.359375 -5.984375 C 24.734375 -6.785156 25.835938 -7.953125 26.671875 -9.484375 C 27.503906 -11.015625 27.921875 -12.789062 27.921875 -14.8125 Z M 27.921875 -14.8125 " }) }) }) }),
        /* @__PURE__ */ jsx("g", { fill: "currentColor", fillOpacity: "1", children: /* @__PURE__ */ jsx("g", { transform: "translate(143.259256, 78.49768)", children: /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx("path", { d: "M 30.4375 -29.359375 L 12.421875 13.796875 L 6.125 13.796875 L 12.09375 -0.484375 L 0.53125 -29.359375 L 7.296875 -29.359375 L 15.5625 -6.984375 L 24.140625 -29.359375 Z M 30.4375 -29.359375 " }) }) }) }),
        /* @__PURE__ */ jsx("g", { fill: "currentColor", fillOpacity: "1", children: /* @__PURE__ */ jsx("g", { transform: "translate(174.281707, 78.49768)", children: /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx("path", { d: "M 10.078125 -39.4375 L 10.078125 0 L 4 0 L 4 -39.4375 Z M 10.078125 -39.4375 " }) }) }) }),
        /* @__PURE__ */ jsx("g", { fill: "currentColor", fillOpacity: "1", children: /* @__PURE__ */ jsx("g", { transform: "translate(188.353752, 78.49768)", children: /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx("path", { d: "M 16.734375 0.484375 C 13.960938 0.484375 11.457031 -0.144531 9.21875 -1.40625 C 6.976562 -2.664062 5.21875 -4.441406 3.9375 -6.734375 C 2.664062 -9.035156 2.03125 -11.691406 2.03125 -14.703125 C 2.03125 -17.691406 2.6875 -20.335938 4 -22.640625 C 5.3125 -24.953125 7.101562 -26.726562 9.375 -27.96875 C 11.65625 -29.21875 14.195312 -29.84375 17 -29.84375 C 19.8125 -29.84375 22.351562 -29.21875 24.625 -27.96875 C 26.894531 -26.726562 28.6875 -24.953125 30 -22.640625 C 31.320312 -20.335938 31.984375 -17.691406 31.984375 -14.703125 C 31.984375 -11.722656 31.304688 -9.078125 29.953125 -6.765625 C 28.597656 -4.453125 26.757812 -2.664062 24.4375 -1.40625 C 22.113281 -0.144531 19.546875 0.484375 16.734375 0.484375 Z M 16.734375 -4.796875 C 18.296875 -4.796875 19.757812 -5.164062 21.125 -5.90625 C 22.5 -6.65625 23.613281 -7.773438 24.46875 -9.265625 C 25.320312 -10.765625 25.75 -12.578125 25.75 -14.703125 C 25.75 -16.835938 25.335938 -18.640625 24.515625 -20.109375 C 23.703125 -21.585938 22.617188 -22.695312 21.265625 -23.4375 C 19.910156 -24.1875 18.453125 -24.5625 16.890625 -24.5625 C 15.328125 -24.5625 13.878906 -24.1875 12.546875 -23.4375 C 11.210938 -22.695312 10.15625 -21.585938 9.375 -20.109375 C 8.59375 -18.640625 8.203125 -16.835938 8.203125 -14.703125 C 8.203125 -11.546875 9.007812 -9.101562 10.625 -7.375 C 12.25 -5.65625 14.285156 -4.796875 16.734375 -4.796875 Z M 16.734375 -4.796875 " }) }) }) }),
        /* @__PURE__ */ jsx("g", { fill: "currentColor", fillOpacity: "1", children: /* @__PURE__ */ jsx("g", { transform: "translate(222.361196, 78.49768)", children: /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx("path", { d: "M 18.8125 -29.84375 C 21.125 -29.84375 23.191406 -29.363281 25.015625 -28.40625 C 26.847656 -27.445312 28.28125 -26.023438 29.3125 -24.140625 C 30.34375 -22.253906 30.859375 -19.984375 30.859375 -17.328125 L 30.859375 0 L 24.84375 0 L 24.84375 -16.421875 C 24.84375 -19.046875 24.179688 -21.054688 22.859375 -22.453125 C 21.546875 -23.859375 19.753906 -24.5625 17.484375 -24.5625 C 15.210938 -24.5625 13.410156 -23.859375 12.078125 -22.453125 C 10.742188 -21.054688 10.078125 -19.046875 10.078125 -16.421875 L 10.078125 0 L 4 0 L 4 -29.359375 L 10.078125 -29.359375 L 10.078125 -26.015625 C 11.066406 -27.222656 12.332031 -28.160156 13.875 -28.828125 C 15.425781 -29.503906 17.070312 -29.84375 18.8125 -29.84375 Z M 18.8125 -29.84375 " }) }) }) }),
        /* @__PURE__ */ jsx(
          "path",
          {
            fill: "currentColor",
            d: "M 53.359375 31.652344 L 53.359375 88.6875 L 62.410156 90.859375 L 62.410156 29.484375 Z M 53.359375 31.652344 ",
            fillOpacity: "1",
            fillRule: "nonzero"
          }
        ),
        /* @__PURE__ */ jsx("g", { clipPath: "url(#38f6fcde47)", children: /* @__PURE__ */ jsx(
          "path",
          {
            fill: "currentColor",
            d: "M 0.339844 47.433594 L 0.339844 72.910156 C 0.339844 73.34375 0.410156 73.769531 0.554688 74.179688 C 0.699219 74.59375 0.90625 74.96875 1.175781 75.3125 C 1.445312 75.65625 1.765625 75.945312 2.132812 76.179688 C 2.503906 76.414062 2.898438 76.582031 3.324219 76.683594 L 9.390625 78.140625 L 9.390625 42.195312 L 3.3125 43.660156 C 2.890625 43.761719 2.492188 43.929688 2.125 44.164062 C 1.761719 44.402344 1.441406 44.6875 1.171875 45.03125 C 0.902344 45.375 0.695312 45.75 0.554688 46.164062 C 0.410156 46.574219 0.339844 46.996094 0.339844 47.433594 Z M 0.339844 47.433594 ",
            fillOpacity: "1",
            fillRule: "nonzero"
          }
        ) }),
        /* @__PURE__ */ jsx("g", { clipPath: "url(#af000f7256)", children: /* @__PURE__ */ jsx(
          "path",
          {
            fill: "currentColor",
            d: "M 64.996094 95.085938 L 64.996094 25.253906 C 64.996094 25.082031 65.027344 24.917969 65.09375 24.761719 C 65.160156 24.601562 65.253906 24.460938 65.375 24.339844 C 65.496094 24.21875 65.636719 24.125 65.792969 24.0625 C 65.953125 23.996094 66.117188 23.960938 66.289062 23.960938 L 71.460938 23.960938 C 71.632812 23.960938 71.796875 23.996094 71.957031 24.0625 C 72.113281 24.125 72.253906 24.21875 72.375 24.339844 C 72.496094 24.460938 72.589844 24.601562 72.65625 24.761719 C 72.722656 24.917969 72.753906 25.082031 72.753906 25.253906 L 72.753906 95.085938 C 72.753906 95.257812 72.722656 95.421875 72.65625 95.582031 C 72.589844 95.738281 72.496094 95.878906 72.375 96 C 72.253906 96.121094 72.113281 96.214844 71.957031 96.28125 C 71.796875 96.347656 71.632812 96.378906 71.460938 96.378906 L 66.289062 96.378906 C 66.117188 96.378906 65.953125 96.347656 65.792969 96.28125 C 65.636719 96.214844 65.496094 96.121094 65.375 96 C 65.253906 95.878906 65.160156 95.738281 65.09375 95.582031 C 65.027344 95.421875 64.996094 95.257812 64.996094 95.085938 Z M 64.996094 95.085938 ",
            fillOpacity: "1",
            fillRule: "nonzero"
          }
        ) }),
        /* @__PURE__ */ jsx(
          "path",
          {
            fill: "currentColor",
            d: "M 22.320312 81.238281 L 22.320312 39.101562 L 11.976562 41.585938 L 11.976562 78.757812 Z M 22.320312 81.238281 ",
            fillOpacity: "1",
            fillRule: "nonzero"
          }
        ),
        /* @__PURE__ */ jsx(
          "path",
          {
            fill: "currentColor",
            d: "M 50.769531 88.066406 L 50.769531 32.277344 L 37.839844 35.378906 L 37.839844 84.960938 Z M 50.769531 88.066406 ",
            fillOpacity: "1",
            fillRule: "nonzero"
          }
        ),
        /* @__PURE__ */ jsx(
          "path",
          {
            fill: "currentColor",
            d: "M 24.90625 81.863281 L 35.253906 84.34375 L 35.253906 35.996094 L 24.90625 38.480469 Z M 24.90625 81.863281 ",
            fillOpacity: "1",
            fillRule: "nonzero"
          }
        )
      ]
    }
  );
};
var logo_default = Logo;

// src/components/global-error-page.tsx
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
function GlobalError({ error, ...rest }) {
  useEffect(() => {
    console.error("Global error:", error);
  }, [error]);
  const reset = () => {
    window.location.reload();
  };
  return /* @__PURE__ */ jsxs2("html", { lang: "en", children: [
    /* @__PURE__ */ jsxs2("head", { children: [
      /* @__PURE__ */ jsx2("meta", { charSet: "utf-8" }),
      /* @__PURE__ */ jsx2("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
      /* @__PURE__ */ jsx2(
        "link",
        {
          rel: "stylesheet",
          href: "/__pylon/static/pylon.css",
          precedence: "high"
        }
      )
    ] }),
    /* @__PURE__ */ jsx2("body", { children: /* @__PURE__ */ jsx2("div", { className: "fixed inset-0 bg-black/90 z-50 overflow-y-auto p-4 flex items-center justify-center", children: /* @__PURE__ */ jsxs2("div", { className: "w-full max-w-3xl bg-black border border-red-600 rounded-lg overflow-hidden text-white font-sans", children: [
      /* @__PURE__ */ jsx2("div", { className: "flex items-center justify-between border-b border-neutral-800 p-4", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3", children: [
        /* @__PURE__ */ jsx2("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx2(logo_default, { className: "h-8 w-auto text-white" }) }),
        /* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2("h1", { className: "text-xl font-medium text-red-500", children: "Application Crashed" }) })
      ] }) }),
      /* @__PURE__ */ jsxs2("div", { className: "p-4", children: [
        /* @__PURE__ */ jsx2("div", { className: "mb-4 text-neutral-400", children: "The application encountered a critical error and could not continue." }),
        /* @__PURE__ */ jsx2("h2", { className: "text-2xl font-bold mb-4 text-white", children: error.message || "A critical error occurred" }),
        error.digest && /* @__PURE__ */ jsxs2("div", { className: "mb-4", children: [
          /* @__PURE__ */ jsx2("h3", { className: "text-sm uppercase tracking-wider text-neutral-500 font-medium mb-2", children: "Error ID" }),
          /* @__PURE__ */ jsx2("div", { className: "bg-neutral-900 rounded-md p-3 text-neutral-300 font-mono", children: error.digest })
        ] })
      ] })
    ] }) }) })
  ] });
}

// src/components/ui/button.tsx
import { Slot } from "@radix-ui/react-slot";
import { cva } from "class-variance-authority";
import { jsx as jsx3 } from "react/jsx-runtime";
var buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
        destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
        outline: "border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
        secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline"
      },
      size: {
        default: "h-9 px-4 py-2 has-[>svg]:px-3",
        sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
        lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
        icon: "size-9"
      }
    },
    defaultVariants: {
      variant: "default",
      size: "default"
    }
  }
);
function Button({
  className,
  variant,
  size,
  asChild = false,
  ...props
}) {
  const Comp = asChild ? Slot : "button";
  return /* @__PURE__ */ jsx3(
    Comp,
    {
      "data-slot": "button",
      className: cn(buttonVariants({ variant, size, className })),
      ...props
    }
  );
}

// src/components/status-page.tsx
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
var StatusPage = ({
  code,
  title,
  message,
  standalone = false,
  returnText = "Return to home",
  returnUrl = "/"
}) => {
  const element = /* @__PURE__ */ jsxs3("div", { className: "flex min-h-screen w-full flex-col items-center justify-center bg-white p-4 text-center", children: [
    /* @__PURE__ */ jsx4("title", { children: title }),
    /* @__PURE__ */ jsx4("h1", { className: "mb-2 text-9xl font-thin tracking-tight text-gray-900", children: code }),
    /* @__PURE__ */ jsx4("h2", { className: "mb-6 text-xl font-light text-gray-600", children: title }),
    /* @__PURE__ */ jsx4("p", { className: "mb-8 max-w-md text-sm text-gray-500", children: message }),
    /* @__PURE__ */ jsx4(Button, { asChild: true, children: /* @__PURE__ */ jsx4("a", { href: returnUrl, children: returnText }) })
  ] });
  if (standalone) {
    return /* @__PURE__ */ jsxs3("html", { children: [
      /* @__PURE__ */ jsxs3("head", { children: [
        /* @__PURE__ */ jsx4("meta", { charSet: "utf-8" }),
        /* @__PURE__ */ jsx4("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
        /* @__PURE__ */ jsx4(
          "link",
          {
            rel: "stylesheet",
            href: "/__pylon/static/pylon.css",
            precedence: "high"
          }
        )
      ] }),
      /* @__PURE__ */ jsx4("body", { children: element })
    ] });
  }
  return element;
};

// src/plugins/use-pages/setup/index.tsx
import { tmpdir } from "os";
import { pipeline } from "stream/promises";
import { __PYLON_INTERNALS_DO_NOT_USE } from "@getcronit/pylon/pages";
import { createHash } from "crypto";
import glob from "tiny-glob/sync.js";

// src/plugins/use-pages/setup/serve-file-path.ts
import { access, stat } from "fs/promises";
import { createReadStream } from "fs";
import mime from "mime";
import { Readable } from "stream";
var serveFilePath = async ({
  filePath,
  context
}) => {
  try {
    await access(filePath);
  } catch (error) {
    return context.notFound();
  }
  try {
    const contentType = mime.getType(filePath) || "application/octet-stream";
    context.header("Content-Type", contentType);
    let options = {};
    let start;
    let end;
    const range = context.req.header("Range");
    if (range) {
      const bytesPrefix = "bytes=";
      if (range.startsWith(bytesPrefix)) {
        const bytesRange = range.substring(bytesPrefix.length);
        const parts = bytesRange.split("-");
        if (parts.length === 2) {
          const rangeStart = parts[0]?.trim();
          if (rangeStart) {
            options.start = start = parseInt(rangeStart, 10);
          }
          const rangeEnd = parts[1]?.trim();
          if (rangeEnd) {
            options.end = end = parseInt(rangeEnd, 10);
          }
        }
      }
    }
    context.header("Accept-Ranges", "bytes");
    const fileStat = await stat(filePath);
    const contentLength = fileStat.size;
    if (context.req.method === "HEAD") {
      context.status(200);
      context.header("Accept-Ranges", "bytes");
      context.header("Content-Length", contentLength.toString());
      return context.body(null, 200);
    }
    let retrievedLength;
    if (start !== void 0 && end !== void 0) {
      retrievedLength = end + 1 - start;
    } else if (start !== void 0) {
      retrievedLength = contentLength - start;
    } else if (end !== void 0) {
      retrievedLength = end + 1;
    } else {
      retrievedLength = contentLength;
    }
    context.status(start !== void 0 || end !== void 0 ? 206 : 200);
    context.header("Content-Length", retrievedLength.toString());
    if (range !== void 0) {
      context.header(
        "Content-Range",
        `bytes ${start || 0}-${end || contentLength - 1}/${contentLength}`
      );
      context.header("Accept-Ranges", "bytes");
    }
    const stream = createReadStream(filePath, options);
    const webStream = Readable.toWeb(stream);
    return context.body(webStream);
  } catch (error) {
    return context.text("Error reading file", 500);
  }
};

// src/plugins/use-pages/setup/index.tsx
import { jsx as jsx5 } from "react/jsx-runtime";
var disableCacheMiddleware = async (c, next) => {
  const env3 = getEnv();
  if (true) {
    c.header(
      "Cache-Control",
      "no-store, no-cache, must-revalidate, proxy-revalidate"
    );
    c.header("Pragma", "no-cache");
    c.header("Expires", "0");
    c.header("Surrogate-Control", "no-store");
  }
  return next();
};
var setup = async (app2) => {
  const cacheBustingSuffix = `?v=${Date.now()}`;
  const routes = (await import(`${process.cwd()}/.pylon/__pylon/pages/app.js`)).default;
  const client = await import(`${process.cwd()}/.pylon/client/index.js`);
  let handler2 = createStaticHandler(routes);
  app2.use(trimTrailingSlash());
  const publicFilesPath = path3.resolve(
    process.cwd(),
    ".pylon",
    "__pylon",
    "public"
  );
  let publicFiles = [];
  try {
    publicFiles = glob(`**/*`, {
      filesOnly: true,
      cwd: publicFilesPath
    });
  } catch (error) {
  }
  app2.on(
    "GET",
    publicFiles.map((file) => `/${file}`),
    disableCacheMiddleware,
    async (c) => {
      const publicFilePath = path3.resolve(
        process.cwd(),
        ".pylon",
        "__pylon",
        "public",
        c.req.path.replace("/", "")
      );
      return serveFilePath({ filePath: publicFilePath, context: c });
    }
  );
  app2.get("/__pylon/static/*", disableCacheMiddleware, async (c) => {
    const filePath = path3.resolve(
      process.cwd(),
      ".pylon",
      "__pylon",
      "static",
      c.req.path.replace("/__pylon/static/", "")
    );
    return serveFilePath({ filePath, context: c });
  });
  app2.get("/__pylon/image", async (c) => {
    try {
      let isSupportedFormat2 = function(format2) {
        const supportedFormats = sharp.format;
        return Object.keys(supportedFormats).includes(format2);
      };
      var isSupportedFormat = isSupportedFormat2;
      const {
        src,
        w,
        h,
        q = "75",
        format = "webp",
        lqip = "false"
      } = c.req.query();
      if (!src) {
        return c.json({ error: "Missing parameters." }, 400);
      }
      const isSrcAbsolute = src.startsWith("http://") || src.startsWith("https://");
      let imagePath;
      if (isSrcAbsolute) {
        imagePath = await downloadImage(src);
      } else {
        if (!src.startsWith("/")) {
          return c.json({ error: "Invalid image path." }, 400);
        }
        if (!src.startsWith("/__pylon/static/media")) {
          imagePath = path3.join(
            process.cwd(),
            ".pylon",
            "__pylon",
            "public",
            src
          );
        } else {
          imagePath = path3.join(process.cwd(), ".pylon", src);
        }
      }
      const cachedImageFileName = getCachedImagePath({
        src,
        width: w ? parseInt(w) : 0,
        height: h ? parseInt(h) : 0,
        quality: q,
        lqip: lqip === "true",
        format
      });
      try {
        await fs2.promises.access(imagePath);
      } catch {
        try {
          imagePath = await downloadImage(src);
        } catch (error) {
          return c.json({ error: "Image not found" }, 404);
        }
      }
      if (IS_IMAGE_CACHE_POSSIBLE) {
        try {
          await fs2.promises.access(cachedImageFileName);
          const stream = fs2.createReadStream(cachedImageFileName);
          c.res.headers.set("Content-Type", getContentType(format));
          return c.body(Readable2.toWeb(stream));
        } catch (e) {
        }
      }
      const sharp = (await import("sharp")).default;
      const metadata = await sharp(imagePath).metadata();
      if (!metadata.width || !metadata.height) {
        return c.json(
          {
            error: "Invalid image metadata. Width and height are required for resizing."
          },
          400
        );
      }
      const { width: finalWidth, height: finalHeight } = calculateDimensions(
        metadata.width,
        metadata.height,
        w ? parseInt(w) : void 0,
        h ? parseInt(h) : void 0
      );
      let imageFormat = format.toLowerCase();
      if (!isSupportedFormat2(imageFormat)) {
        throw new Error("Unsupported image format");
      }
      const quality = parseInt(q);
      let data = sharp(imagePath);
      if (lqip === "true") {
        data = data.resize({
          width: Math.min(finalWidth ?? 16, 16),
          height: Math.min(finalHeight ?? 16, 16),
          fit: "inside"
        }).toFormat("webp", {
          quality: 30,
          alphaQuality: 20,
          smartSubsample: true
        });
      } else {
        data = data.resize(finalWidth, finalHeight).toFormat(imageFormat, {
          quality
        });
      }
      if (IS_IMAGE_CACHE_POSSIBLE) {
        const image = await data.toFile(cachedImageFileName);
        c.res.headers.set("Content-Type", getContentType(image.format));
        return c.body(
          Readable2.toWeb(
            fs2.createReadStream(cachedImageFileName)
          )
        );
      } else {
        const image = await data.toBuffer({ resolveWithObject: true });
        c.res.headers.set("Content-Type", getContentType(image.info.format));
        return c.body(image.data);
      }
    } catch (error) {
      console.error("Error processing the image:", error);
      return c.json({ error: "Error processing the image" }, 500);
    }
  });
  app2.get("*", disableCacheMiddleware, async (c) => {
    const context = await handler2.query(c.req.raw);
    if (context instanceof Response) {
      return context;
    }
    const router = createStaticRouter(handler2.dataRoutes, context);
    const component = /* @__PURE__ */ jsx5(__PYLON_INTERNALS_DO_NOT_USE.DataClientProvider, { client, children: /* @__PURE__ */ jsx5(StaticRouterProvider, { router, context }) });
    if (c.req.header("accept")?.includes("application/json")) {
      const context2 = c.get("pagesContext") || {};
      let cacheSnapshot;
      try {
        client.cache.clear();
        const data = await client.prepareReactRender(component);
        cacheSnapshot = data.cacheSnapshot;
      } catch (error) {
        if (error instanceof Response) {
          return error;
        }
      }
      return c.json({
        cacheSnapshot,
        context: context2
      });
    }
    try {
      if (reactServer.renderToReadableStream) {
        try {
          const stream = await reactServer.renderToReadableStream(component, {
            bootstrapModules: ["/__pylon/static/app.js" + cacheBustingSuffix]
          });
          c.header("Content-Type", "text/html");
          return c.body(stream);
        } catch (error) {
          throw error;
        }
      } else if (reactServer.renderToPipeableStream) {
        return await new Promise((resolve, reject) => {
          const { pipe } = reactServer.renderToPipeableStream(
            component,
            {
              bootstrapModules: ["/__pylon/static/app.js" + cacheBustingSuffix],
              onShellReady: async () => {
                c.header("Content-Type", "text/html");
                const passThrough = new PassThrough();
                pipe(passThrough);
                resolve(c.body(Readable2.toWeb(passThrough)));
              },
              onShellError: async (error) => {
                reject(error);
              }
            }
          );
        });
      } else {
        throw new Error("Environment not supported");
      }
    } catch (errorOrResponse) {
      c.header("Content-Type", "text/html");
      if (errorOrResponse instanceof Response) {
        c.status(errorOrResponse.status);
        if (errorOrResponse.status >= 300 && errorOrResponse.status < 400) {
          const location = errorOrResponse.headers.get("Location");
          if (location) {
            return c.redirect(
              location,
              errorOrResponse.status
            );
          }
        }
        return c.html(
          reactServer.renderToString(
            /* @__PURE__ */ jsx5(
              StatusPage,
              {
                code: errorOrResponse.status,
                title: errorOrResponse.statusText,
                message: errorOrResponse.statusText,
                standalone: true
              }
            )
          )
        );
      }
      c.status(500);
      return c.html(
        reactServer.renderToString(
          /* @__PURE__ */ jsx5(GlobalError, { error: errorOrResponse })
        )
      );
    }
  });
};
var IMAGE_CACHE_DIR = path3.join(process.cwd(), ".cache/__pylon/images");
var IS_IMAGE_CACHE_POSSIBLE = true;
try {
  await fs2.promises.mkdir(IMAGE_CACHE_DIR, { recursive: true });
} catch (error) {
  IS_IMAGE_CACHE_POSSIBLE = false;
}
var getCachedImagePath = (args) => {
  const fileName = `${path3.basename(
    createHash("md5").update(JSON.stringify(args)).digest("hex"),
    path3.extname(args.src)
  )}-${args.width}x${args.height}.${args.format}`;
  return path3.join(IMAGE_CACHE_DIR, fileName);
};
var calculateDimensions = (originalWidth, originalHeight, width, height) => {
  if (!width && !height) {
    return { width: originalWidth, height: originalHeight };
  }
  if (width && !height) {
    height = Math.round(width * originalHeight / originalWidth);
  } else if (height && !width) {
    width = Math.round(height * originalWidth / originalHeight);
  }
  return { width, height };
};
var getContentType = (format) => {
  switch (format.toLowerCase()) {
    case "webp":
      return "image/webp";
    case "jpeg":
    case "jpg":
      return "image/jpeg";
    case "png":
      return "image/png";
    case "gif":
      return "image/gif";
    case "svg":
      return "image/svg+xml";
    default:
      return "application/octet-stream";
  }
};
var downloadImage = async (url) => {
  const isSrcAbsoluteUrl = url.startsWith("http://") || url.startsWith("https://");
  const _fetch = isSrcAbsoluteUrl ? fetch : app.request;
  const response = await _fetch(url);
  if (!response.ok)
    throw new Error(`Failed to download image: ${response.statusText}`);
  const ext = path3.extname(url) || ".jpg";
  const tempFilePath = path3.join(tmpdir(), `image-${Date.now()}${ext}`);
  const fileStream = fs2.createWriteStream(tempFilePath);
  await pipeline(response.body, fileStream);
  return tempFilePath;
};

// src/plugins/use-pages/build/index.ts
import path7 from "path";

// src/plugins/use-pages/build/app-utils.ts
import fs3 from "fs";
import path4 from "path";
var PAGES_DIR = "./pages";
var imports = [];
var routeSlugs = [];
function getLayoutComponentName(filePath) {
  return filePath.replace(PAGES_DIR, "").replace(/\\/g, "/").replace(/layout\.tsx$/, "").split("/").filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join("") + "Layout";
}
function convertToDynamicRoute(segment) {
  if (segment.startsWith("[") && segment.endsWith("]")) {
    return `:${segment.slice(1, -1)}`;
  }
  return segment;
}
function scanDirectory(directory, basePath = "") {
  const items = fs3.readdirSync(directory, { withFileTypes: true });
  const route = { path: basePath || "/", children: [] };
  let hasLayout = false;
  let pageFound = false;
  for (const item of items) {
    const itemPath = path4.join(directory, item.name);
    const relativePath = path4.join(basePath, item.name).replace(/\\/g, "/");
    const importPath = `"./${path4.join("..", PAGES_DIR, relativePath).replace(/\.tsx$/, "")}"`;
    if (item.isDirectory()) {
      const childRoute = scanDirectory(itemPath, relativePath);
      if (childRoute) {
        route.children.push(childRoute);
      }
    } else if (item.name === "layout.tsx") {
      const layoutComponentName = getLayoutComponentName(relativePath);
      imports.push(`import ${layoutComponentName} from ${importPath};`);
      const componentName = layoutComponentName === "Layout" ? `RootLayout` : `${layoutComponentName}`;
      route.Component = `withLoaderData((props) => <${componentName} children={<Outlet />} {...props} />)`;
      route.loader = `loader`;
      route.shouldRevalidate = `() => false`;
      if (route.path === "/") {
        route.errorElement = "<ErrorElement standalone={true} />";
      }
      route.HydrateFallback = "HydrateFallback";
      hasLayout = true;
    } else if (item.name === "page.tsx") {
      route.children.push({
        path: void 0,
        index: true,
        errorElement: "<ErrorElement standalone={false} />",
        lazy: `async () => {const i = await import(${importPath}).catch(() => {window.reload()}); return {Component: withLoaderData(i.default)}}`,
        HydrateFallback: "HydrateFallback",
        loader: `loader`
      });
      pageFound = true;
    }
  }
  if (route.path) {
    const segments = route.path.split("/").map((segment) => convertToDynamicRoute(segment)).filter(Boolean);
    const fullPath = segments.length > 0 ? `/${segments.join("/")}` : "/";
    route.path = segments[segments.length - 1] || "/";
    if (hasLayout || pageFound) {
      routeSlugs.push(fullPath);
    }
  }
  if (hasLayout) {
    const childNotFoundRoute = {
      path: "*",
      element: "<NotFoundPage standalone={false} />"
    };
    if (!route.children) {
      route.children = [];
    }
    route.children.push(childNotFoundRoute);
  }
  if (hasLayout || route.lazy || route.children && route.children.length > 0) {
    return route;
  }
  return null;
}
function serialize(obj, parentKey) {
  if (Array.isArray(obj)) {
    return `[${obj.map(serialize).join(", ")}]`;
  } else if (obj && typeof obj === "object") {
    const entries = Object.entries(obj).map(
      ([key, value]) => `${JSON.stringify(key)}: ${serialize(value, key)}`
    );
    return `{${entries.join(", ")}}`;
  } else if (typeof obj === "string") {
    if (parentKey === "lazy" || parentKey === "loader" || parentKey === "shouldRevalidate" || parentKey === "Component" || parentKey === "element" || parentKey === "errorElement" || parentKey === "HydrateFallback") {
      return obj;
    }
    return JSON.stringify(obj);
  } else {
    return String(obj);
  }
}
function makeAppFiles() {
  imports = [];
  routeSlugs = [];
  const rootRoute = scanDirectory(PAGES_DIR);
  const notFoundRoute = {
    path: "*",
    element: "<NotFoundPage standalone={true} />"
  };
  const routes = `${imports.join("\n")}

import {useMemo} from 'react'

import {__PYLON_ROUTER_INTERNALS_DO_NOT_USE, __PYLON_INTERNALS_DO_NOT_USE, GlobalErrorPage, StatusPage} from '@getcronit/pylon/pages'
const Outlet = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.Outlet

const ErrorElement: React.FC<{standalone: boolean}> = ({standalone}) => {
  const error = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.useRouteError()


    if(error instanceof Response) {
      // Check if the error is a redirect response
      if(error.status > 300 && error.status < 400 && error.headers.get('Location')) {
      return <__PYLON_ROUTER_INTERNALS_DO_NOT_USE.Navigate to={error.headers.get('Location')!} replace />
      }

      let message = 'An unexpected error occurred.'

    try {
      const data = JSON.parse(error.data?.message || '{}')
      if (data.message) {
        message = data.message
      }
    } catch (e) {}

    return (
      <StatusPage
        code={error.status}
        title={error.statusText}
        message={message}
        standalone={standalone}
      />
    )
  }

  return <GlobalErrorPage error={error} />
}

const HydrateFallback = () => {
  return <div>Loading...</div>
}

function withLoaderData<T>(Component: React.ComponentType<{ data: T }>) {
  return function WithLoaderDataWrapper(props: T) {
    const dataClient = __PYLON_INTERNALS_DO_NOT_USE.useDataClient()
    const {useQuery, useHydrateCache} = useMemo(() => dataClient.pageClient(), [])


    const {cacheSnapshot, context} = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.useLoaderData() || {};

    const location = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.useLocation()
    const [searchParams] = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.useSearchParams()
    const searchParamsObject = Object.fromEntries(searchParams.entries())
    const params = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.useParams()

    if(cacheSnapshot) {
      useHydrateCache({cacheSnapshot})
    }

    const data = typeof window !== "undefined" ? useQuery() : dataClient.useQuery()

    return <Component {...(props as any)} path={location.pathname} params={params} searchParams={searchParamsObject} data={data} context={context} />;
  };
}

const loader: __PYLON_ROUTER_INTERNALS_DO_NOT_USE.LoaderFunction = async ({ request }) => {
  // 1. Skip if request is a JSON-only fetch (e.g., client-side route preloading)
  const acceptHeader = request.headers.get('accept')
  if (acceptHeader?.includes('application/json')) {
    return null
  }

  const url = new URL(request.url)
  const headers = new Headers()
  let fetchToUse: typeof fetch = fetch

  try {
    // 2. Try importing Pylon \u2014 if this works, we're on the server
    const moduleNameToPreventBundling = '@getcronit/pylon'
    const { app, getContext } = await import(moduleNameToPreventBundling)
    fetchToUse = app.request

    // 3. Get headers from the original server request and forward them
    const context = getContext()
    for (const [key, value] of context.req.raw.headers.entries()) {
      headers.append(key, value)
    }
  } catch {
    // 4. Pylon not available \u2014 fallback to default fetch (runs in browser)
    // No additional headers are needed; browser sends cookies automatically
  }

  headers.set('Accept', 'application/json') // Ensure the internal request gets JSON

  const response = await fetchToUse(url.pathname + url.search, {
      method: 'GET',
      headers,
  })

  try {
    const data = await response.json<object>()
    return data
  } catch {
    return null
  }
}


const RootLayout = (props: { children: React.ReactNode; [key: string]: any }) => {
  return (
    <Layout {...props}>
      <meta charSet="utf-8" />
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      <link rel="stylesheet" href="/__pylon/static/pylon.css" precedence="high" />
      <link rel="stylesheet" href="/__pylon/static/app.css" precedence="high" />
      {props.children}
    </Layout>
  )
}

const NotFoundPage: React.FC<{standalone: boolean}> = ({standalone = false}) => {
  return <StatusPage code={404} title="Page Not Found" message="The page you are looking for does not exist." standalone={standalone} />
}

const routes = ${serialize([rootRoute, notFoundRoute].filter(Boolean))}

export default routes

`;
  const slugs = `export default ${JSON.stringify(routeSlugs, null, 2)}`;
  return {
    routes,
    slugs
  };
}

// src/plugins/use-pages/build/index.ts
import chokidar from "chokidar";
import fs7 from "fs/promises";
import esbuild from "esbuild";

// src/plugins/use-pages/build/plugins/inject-app-hydration.ts
import path5 from "path";
import fs4 from "fs/promises";
var injectAppHydrationPlugin = {
  name: "inject-hydration",
  setup(build2) {
    build2.onLoad({ filter: /.*/, namespace: "file" }, async (args) => {
      if (args.path === path5.resolve(process.cwd(), ".pylon", "app.tsx")) {
        let contents = await fs4.readFile(args.path, "utf-8");
        const clientPath = path5.resolve(process.cwd(), ".pylon/client");
        const pathToClient = path5.relative(path5.dirname(args.path), clientPath);
        contents += `
          import {hydrateRoot} from 'react-dom/client'
          import * as client from './${pathToClient}'
          import { __PYLON_ROUTER_INTERNALS_DO_NOT_USE, __PYLON_INTERNALS_DO_NOT_USE, DevOverlay, onCaughtErrorProd, onRecoverableErrorProd, onUncaughtErrorProd } from '@getcronit/pylon/pages';
          const {createBrowserRouter, RouterProvider, matchRoutes} = __PYLON_ROUTER_INTERNALS_DO_NOT_USE
          const {DataClientProvider} = __PYLON_INTERNALS_DO_NOT_USE
          import React, {useMemo, startTransition} from 'react'
          import * as Sentry from '@sentry/react'

          

          async function hydrate() {
            // Determine if any of the initial routes are lazy
            const lazyMatches = matchRoutes(routes, window.location)?.filter(
              (m) => m.route.lazy
            );

            // Load the lazy matches and update the routes before creating your router
            // so we can hydrate the SSR-rendered content synchronously
            if (lazyMatches && lazyMatches?.length > 0) {
              await Promise.all(
                lazyMatches.map(async (m) => {
                  const routeModule = await m.route.lazy!();
                  Object.assign(m.route, { ...routeModule, lazy: undefined });
                })
              );
            }

            const router = createBrowserRouter(routes)

            startTransition(() => {
              hydrateRoot(
                document,
                <DataClientProvider client={client}>
                  <RouterProvider router={router} />
                </DataClientProvider>
              , {
                // Callback called when an error is thrown and not caught by an ErrorBoundary.
                onUncaughtError: Sentry.reactErrorHandler((error, errorInfo) => {
                  console.warn('Uncaught error', error, errorInfo.componentStack);
                }),
                // Callback called when React catches an error in an ErrorBoundary.
                onCaughtError: Sentry.reactErrorHandler(),
                // Callback called when React automatically recovers from errors.
                onRecoverableError: Sentry.reactErrorHandler(),
              })
            })
         }

         hydrate()


          `;
        return {
          loader: "tsx",
          contents
        };
      }
    });
  }
};

// src/plugins/use-pages/build/plugins/image-plugin.ts
import { createHash as createHash2 } from "crypto";
import path6 from "path";
import fs5 from "fs/promises";
var imagePlugin = {
  name: "image-plugin",
  setup(build2) {
    const outdir = build2.initialOptions.outdir;
    const publicPath = build2.initialOptions.publicPath;
    if (!outdir || !publicPath) {
      throw new Error("outdir and publicPath must be set in esbuild options");
    }
    build2.onResolve({ filter: /\.(png|jpe?g)$/ }, async (args) => {
      const filePath = path6.resolve(args.resolveDir, args.path);
      const fileName = path6.basename(filePath);
      const extname = path6.extname(filePath);
      const hash = createHash2("md5").update(filePath + await fs5.readFile(filePath)).digest("hex").slice(0, 8);
      const newFilename = `${fileName}-${hash}${extname}`;
      const newFilePath = path6.join(outdir, "media", newFilename);
      await fs5.mkdir(path6.dirname(newFilePath), { recursive: true });
      await fs5.copyFile(filePath, newFilePath);
      return {
        path: newFilePath,
        namespace: "image"
      };
    });
    build2.onLoad({ filter: /\.png$|\.jpg$/ }, async (args) => {
      const sharp = (await import("sharp")).default;
      const image = sharp(args.path);
      const metadata = await image.metadata();
      const url = `${publicPath}/media/${path6.basename(args.path)}`;
      const output = image.resize({
        width: Math.min(metadata.width ?? 16, 16),
        height: Math.min(metadata.height ?? 16, 16),
        fit: "inside"
      }).toFormat("webp", {
        quality: 30,
        alphaQuality: 20,
        smartSubsample: true
      });
      const { data, info } = await output.toBuffer({ resolveWithObject: true });
      const dataURIBase64 = `data:image/${info.format};base64,${data.toString(
        "base64"
      )}`;
      return {
        contents: JSON.stringify({
          url,
          width: metadata.width,
          height: metadata.height,
          blurDataURL: dataURIBase64
        }),
        loader: "json"
      };
    });
  }
};

// src/plugins/use-pages/build/plugins/postcss-plugin.ts
import fs6 from "fs/promises";
import loadConfig from "postcss-load-config";
import postcss from "postcss";
var postcssPlugin = {
  name: "postcss-plugin",
  setup(build2) {
    build2.onLoad({ filter: /.css$/, namespace: "file" }, async (args) => {
      const { plugins, options } = await loadConfig();
      const css = await fs6.readFile(args.path, "utf-8");
      const result = await postcss(plugins).process(css, {
        ...options,
        from: args.path
      }).then((result2) => result2);
      return {
        contents: result.css,
        loader: "css"
      };
    });
  }
};

// src/plugins/use-pages/build/plugins/external-esm-plugin.ts
import escapeStringRegexp from "escape-string-regexp";
var NAME = "esm-externals";
var NAMESPACE = NAME;
function makeFilter(externals) {
  return new RegExp(
    "^(" + externals.map(escapeStringRegexp).join("|") + ")(\\/.*)?$"
    // TODO support for query strings?
  );
}
var esmExternalsPlugin = (externals) => {
  return {
    name: NAME,
    setup(build2) {
      const filter = makeFilter(externals);
      build2.onResolve({ filter: /.*/, namespace: NAMESPACE }, (args) => {
        return {
          path: args.path,
          external: true
        };
      });
      build2.onResolve({ filter }, (args) => {
        return {
          path: args.path,
          namespace: NAMESPACE
        };
      });
      build2.onLoad({ filter: /.*/, namespace: NAMESPACE }, (args) => {
        return {
          contents: `export * as default from ${JSON.stringify(
            args.path
          )}; export * from ${JSON.stringify(args.path)};`
        };
      });
    }
  };
};

// src/plugins/use-pages/build/index.ts
var DIST_STATIC_DIR = path7.join(process.cwd(), ".pylon/__pylon/static");
var DIST_PAGES_DIR = path7.join(process.cwd(), ".pylon/__pylon/pages");
async function updateFileIfChanged(path8, newContent) {
  try {
    const currentContent = await fs7.readFile(path8);
    if (currentContent.equals(newContent)) {
      return false;
    }
  } catch (err) {
    if (err.code !== "ENOENT") throw err;
  }
  await fs7.writeFile(path8, newContent);
  return true;
}
var build = async () => {
  const buildAppFile = async () => {
    const appFiles = makeAppFiles();
    await updateFileIfChanged(
      path7.resolve(process.cwd(), ".pylon", "app.tsx"),
      Buffer.from(appFiles.routes)
    );
  };
  const copyPublicDir = async () => {
    const publicDir = path7.resolve(process.cwd(), "public");
    const pylonPublicDir = path7.resolve(
      process.cwd(),
      ".pylon",
      "__pylon",
      "public"
    );
    try {
      await fs7.access(publicDir);
      await fs7.mkdir(pylonPublicDir, { recursive: true });
      await fs7.cp(publicDir, pylonPublicDir, { recursive: true });
    } catch (err) {
      if (err.code !== "ENOENT") throw err;
    }
  };
  const copyPylonCSS = async () => {
    const pylonCssPathDir = path7.join(
      process.cwd(),
      "node_modules",
      "@getcronit/pylon/dist/pages"
    );
    const pylonCssDestDir = path7.join(
      process.cwd(),
      ".pylon",
      "__pylon",
      "static"
    );
    await fs7.mkdir(pylonCssDestDir, { recursive: true });
    await fs7.cp(
      path7.join(pylonCssPathDir, "index.css"),
      path7.join(pylonCssDestDir, "pylon.css")
    );
    await fs7.cp(
      path7.join(pylonCssPathDir, "index.css.map"),
      path7.join(pylonCssDestDir, "pylon.css.map")
    );
  };
  const writeOnEndPlugin = {
    name: "write-on-end",
    setup(build2) {
      build2.onEnd(async (result) => {
        await Promise.all(
          result.outputFiles.map(async (file) => {
            await fs7.mkdir(path7.dirname(file.path), { recursive: true });
            await updateFileIfChanged(file.path, file.contents);
          })
        );
      });
    }
  };
  const nodePaths = [
    path7.join(process.cwd(), "node_modules"),
    path7.join(process.cwd(), "node_modules", "@getcronit/pylon/node_modules")
  ];
  let pagesWatcher = null;
  const timePlugin = (name) => ({
    name: "rebuild-log",
    setup({ onStart, onEnd }) {
      var t;
      onStart(() => {
        t = Date.now();
      });
      onEnd(() => {
        console.log(`Pages [${name}] Rebuild took ${Date.now() - t}ms`);
      });
    }
  });
  const clientCtx = await esbuild.context({
    sourcemap: "linked",
    write: false,
    metafile: true,
    nodePaths,
    absWorkingDir: process.cwd(),
    plugins: [
      injectAppHydrationPlugin,
      imagePlugin,
      postcssPlugin,
      writeOnEndPlugin,
      timePlugin("client")
    ],
    publicPath: "/__pylon/static",
    assetNames: "assets/[name]-[hash]",
    chunkNames: "chunks/[name]-[hash]",
    format: "esm",
    platform: "browser",
    entryPoints: [".pylon/app.tsx"],
    outdir: DIST_STATIC_DIR,
    bundle: true,
    splitting: true,
    minify: false,
    loader: {
      // Map file extensions to the file loader
      ".svg": "file",
      ".woff": "file",
      ".woff2": "file",
      ".ttf": "file",
      ".otf": "file"
    },
    define: {
      "process.env.NODE_ENV": JSON.stringify(
        process.env.NODE_ENV || "development"
      )
    },
    mainFields: ["browser", "module", "main"]
  });
  const serverCtx = await esbuild.context({
    sourcemap: "inline",
    write: false,
    absWorkingDir: process.cwd(),
    nodePaths,
    plugins: [
      imagePlugin,
      postcssPlugin,
      writeOnEndPlugin,
      timePlugin("server"),
      esmExternalsPlugin([
        "@getcronit/pylon",
        "react",
        "react-dom",
        "gqty",
        "@gqty/react"
      ])
    ],
    publicPath: "/__pylon/static",
    assetNames: "assets/[name]-[hash]",
    chunkNames: "chunks/[name]-[hash]",
    format: "esm",
    platform: "node",
    entryPoints: [".pylon/app.tsx"],
    outdir: DIST_PAGES_DIR,
    bundle: true,
    splitting: false,
    external: ["@getcronit/pylon", "react", "react-dom", "gqty", "@gqty/react"],
    minify: true,
    loader: {
      // Map file extensions to the file loader
      ".svg": "file",
      ".woff": "file",
      ".woff2": "file",
      ".ttf": "file",
      ".otf": "file"
    },
    define: {
      "process.env.NODE_ENV": JSON.stringify(
        process.env.NODE_ENV || "development"
      )
    },
    mainFields: ["module", "main"]
  });
  return {
    watch: async () => {
      await buildAppFile();
      await copyPublicDir();
      await copyPylonCSS();
      pagesWatcher = chokidar.watch("pages", { ignoreInitial: true });
      pagesWatcher.on("all", async (event, path8) => {
        if (["add", "change", "unlink"].includes(event)) {
          await buildAppFile();
          await copyPublicDir();
          await copyPylonCSS();
        }
      });
      await Promise.all([clientCtx.watch(), serverCtx.watch()]);
    },
    dispose: async () => {
      if (pagesWatcher) {
        pagesWatcher.close();
      }
      Promise.all([clientCtx.dispose(), serverCtx.dispose()]);
    },
    rebuild: async () => {
      await buildAppFile();
      await copyPublicDir();
      await copyPylonCSS();
      await Promise.all([clientCtx.rebuild(), serverCtx.rebuild()]);
      return {};
    },
    cancel: async () => {
      if (pagesWatcher) {
        await pagesWatcher.close();
      }
      await Promise.all([clientCtx.cancel(), serverCtx.cancel()]);
    }
  };
};

// src/plugins/use-pages/index.ts
function usePages() {
  return {
    strategy: "last",
    setup,
    build
  };
}
export {
  ServiceError,
  app,
  asyncContext,
  authMiddleware,
  createDecorator,
  executeConfig,
  createPubSub as experimentalCreatePubSub,
  getContext,
  getEnv,
  handler,
  requireAuth,
  setContext,
  useAuth,
  usePages
};
//# sourceMappingURL=index.js.map