@getcronit/pylon
Version:

3,359 lines • 128 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
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');
});
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/context.ts
import { AsyncLocalStorage } from "async_hooks";
import { env } from "hono/adapter";
var asyncContext, getContext, setContext;
var init_context = __esm({
"src/context.ts"() {
"use strict";
asyncContext = new AsyncLocalStorage();
getContext = () => {
const ctx = asyncContext.getStore();
if (!ctx) {
throw new Error("Context not defined");
}
ctx.env = env(ctx);
return ctx;
};
setContext = (context) => {
return asyncContext.enterWith(context);
};
}
});
// src/app/index.ts
import { sentry } from "@hono/sentry";
import { Hono } from "hono";
import { except } from "hono/combine";
import { compress } from "hono/compress";
import { logger } from "hono/logger";
var app, skipInternal, pluginsMiddleware, pluginsMiddlewareLoader;
var init_app = __esm({
"src/app/index.ts"() {
"use strict";
init_context();
app = new Hono();
skipInternal = (middleware) => {
return async (c, next) => {
if (c.req.header("X-Pylon-Internal") === "true") {
return next();
}
return middleware(c, next);
};
};
app.use("*", skipInternal(compress()));
app.use("*", skipInternal(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("*", skipInternal(except(["/__pylon/*"], logger())));
pluginsMiddleware = [];
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/define-pylon.ts
import * as Sentry from "@sentry/bun";
import consola from "consola";
import {
getNamedType,
GraphQLError,
GraphQLUnionType,
isInterfaceType,
isNonNullType,
isObjectType,
Kind
} from "graphql";
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
var uniqueFieldsCache, selectionSetCache, executionAsyncContext, getResolveInfo, getSelectedFields, wrapResolver, resolversToGraphQLResolvers, ServiceError;
var init_define_pylon = __esm({
"src/define-pylon.ts"() {
"use strict";
init_context();
uniqueFieldsCache = /* @__PURE__ */ new WeakMap();
selectionSetCache = /* @__PURE__ */ new WeakMap();
executionAsyncContext = new AsyncLocalStorage2();
getResolveInfo = () => {
const store = executionAsyncContext.getStore();
if (!store) {
throw new Error("Resolve info is not available");
}
return store;
};
getSelectedFields = (info, fieldNodes = info.fieldNodes, parentType) => {
let parentCache = selectionSetCache.get(fieldNodes);
if (!parentCache) {
parentCache = /* @__PURE__ */ new Map();
selectionSetCache.set(fieldNodes, parentCache);
}
if (parentCache.has(parentType)) {
return parentCache.get(parentType);
}
const fieldsMap = /* @__PURE__ */ new Map();
const extract = (selections, currentType) => {
for (const selection of selections) {
if (selection.kind === Kind.FIELD) {
const name = selection.name.value;
let childReturnType = void 0;
if (currentType && (isObjectType(currentType) || isInterfaceType(currentType))) {
const fieldDef = currentType.getFields()[name];
if (fieldDef) {
childReturnType = getNamedType(fieldDef.type);
}
}
if (!fieldsMap.has(name)) {
fieldsMap.set(name, { nodes: [], returnType: childReturnType });
}
fieldsMap.get(name).nodes.push(selection);
} else if (selection.kind === Kind.INLINE_FRAGMENT) {
const inlineType = selection.typeCondition ? info.schema.getType(selection.typeCondition.name.value) : currentType;
if (selection.selectionSet) {
extract(selection.selectionSet.selections, inlineType);
}
} else if (selection.kind === Kind.FRAGMENT_SPREAD) {
const fragment = info.fragments[selection.name.value];
if (fragment && fragment.selectionSet) {
const fragmentType = info.schema.getType(
fragment.typeCondition.name.value
);
extract(fragment.selectionSet.selections, fragmentType);
}
}
}
};
for (const fieldNode of fieldNodes) {
if (fieldNode.selectionSet) {
extract(fieldNode.selectionSet.selections, parentType);
}
}
const result = Array.from(fieldsMap.entries()).map(([name, data]) => ({
name,
fieldNodes: data.nodes,
returnType: data.returnType
}));
if (parentType && (isInterfaceType(parentType) || parentType instanceof GraphQLUnionType)) {
const abstractType = info.schema.getType(parentType.name);
const possibleTypes = info.schema.getPossibleTypes(abstractType);
for (const possibleType of possibleTypes) {
let uniqueField = uniqueFieldsCache.get(possibleType)?.[0];
const typeFieldsMap = possibleType.getFields();
if (uniqueField === void 0) {
const typeFields = Object.keys(typeFieldsMap).filter(
(f) => isNonNullType(typeFieldsMap[f].type)
);
const otherTypes = possibleTypes.filter(
(t) => t.name !== possibleType.name
);
const otherTypesFields = new Set(
otherTypes.flatMap((t) => Object.keys(t.getFields()))
);
const foundUniqueField = typeFields.find((f) => !otherTypesFields.has(f));
if (foundUniqueField) {
uniqueFieldsCache.set(possibleType, [foundUniqueField]);
uniqueField = foundUniqueField;
} else {
uniqueFieldsCache.set(possibleType, []);
uniqueField = void 0;
}
}
if (uniqueField && !result.some((f) => f.name === uniqueField)) {
result.push({
name: uniqueField,
fieldNodes: [],
returnType: getNamedType(typeFieldsMap[uniqueField].type)
});
}
}
}
parentCache.set(parentType, result);
return result;
};
wrapResolver = (resolver, context, fieldNodes, parentType) => {
if (resolver === null || typeof resolver !== "object" && typeof resolver !== "function") {
return resolver;
}
if (resolver instanceof Date) {
return resolver;
}
if (typeof resolver.then === "function") {
return resolver.then(
(resolved) => wrapResolver(resolved, context, fieldNodes, parentType)
);
}
if (Array.isArray(resolver)) {
const results = resolver.map(
(item) => wrapResolver(item, context, fieldNodes, parentType)
);
if (results.some((r) => r && typeof r.then === "function")) {
return Promise.all(results);
}
return results;
}
if (typeof resolver === "function") {
return (args, ctx, info) => {
const currentParentType = getNamedType(info.returnType);
const selectedFields2 = getSelectedFields(
info,
fieldNodes || info.fieldNodes,
currentParentType
);
const executionContext = { info, selectedFields: selectedFields2 };
return executionAsyncContext.run(executionContext, () => {
const fieldDef = info.parentType.getFields()[info.fieldName];
const orderedArgs = fieldDef ? fieldDef.args.map(
(arg) => args[arg.name] !== void 0 ? args[arg.name] : arg.defaultValue
) : [];
return wrapResolver(
resolver(...orderedArgs),
executionContext,
fieldNodes || info.fieldNodes,
currentParentType
);
});
};
}
const selectedFields = getSelectedFields(
context.info,
fieldNodes || context.info.fieldNodes,
parentType
);
if (selectedFields.length === 0) {
return resolver;
}
const result = {};
for (const { name, fieldNodes: childNodes, returnType } of selectedFields) {
const hasAliases = childNodes.some((node) => node.alias !== void 0);
if (hasAliases) {
result[name] = (args, ctx, info) => {
const aliasKey = info.fieldNodes[0].alias?.value;
const schemaKey = info.fieldName;
if (aliasKey && resolver[aliasKey] !== void 0) {
return wrapResolver(
resolver[aliasKey],
context,
info.fieldNodes,
returnType
);
}
const schemaValue = resolver[schemaKey];
if (schemaValue !== void 0) {
return wrapResolver(schemaValue, context, info.fieldNodes, returnType);
}
return void 0;
};
} else {
const rawValue = resolver[name];
if (rawValue !== void 0) {
result[name] = wrapResolver(rawValue, context, childNodes, returnType);
}
}
}
return result;
return result;
};
resolversToGraphQLResolvers = (resolvers, configureContext) => {
const rootGraphqlResolver = (resolver) => 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
});
}
const rootParentType = getNamedType(info.returnType);
const selectedFields = getSelectedFields(
info,
info.fieldNodes,
rootParentType
);
const executionContext = { info, selectedFields };
return executionAsyncContext.run(executionContext, async () => {
const wrapped = wrapResolver(
resolver,
executionContext,
info.fieldNodes,
rootParentType
);
if (typeof wrapped === "function") {
return wrapped(args, ctx2, info);
}
return wrapped;
});
});
};
const graphqlResolvers = {};
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;
};
ServiceError = class extends GraphQLError {
extensions;
constructor(message, extensions, error) {
super(message, {
originalError: error
});
this.extensions = extensions;
this.cause = error;
}
};
}
});
// src/plugins/use-sentry.ts
import { Kind as Kind2, print } from "graphql";
import {
getDocumentString,
handleStreamOrSingleExecutionResult,
isOriginalGraphQLError
} from "@envelop/core";
import * as Sentry2 from "@sentry/node";
var defaultSkipError, useSentry;
var init_use_sentry = __esm({
"src/plugins/use-sentry.ts"() {
"use strict";
defaultSkipError = isOriginalGraphQLError;
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 === Kind2.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);
}
};
}
};
};
}
});
// package.json
var version;
var init_package = __esm({
"package.json"() {
version = "3.0.0-canary-20260317113404.1a39ad4b34ee59425656d7efe6deca94c22a84a8";
}
});
// src/plugins/use-unhandled-route.ts
import { html } from "hono/html";
function useUnhandledRoute() {
return {
setup: (app2) => {
app2.notFound((c) => {
return c.html(
html`<!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
);
});
}
};
}
var init_use_unhandled_route = __esm({
"src/plugins/use-unhandled-route.ts"() {
"use strict";
init_package();
}
});
// src/plugins/use-viewer.ts
import { html as html2 } from "hono/html";
function useViewer() {
return {
setup: (app2) => {
app2.get("/viewer", async (c) => {
return c.html(
await html2`
<!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>
`
);
});
}
};
}
var init_use_viewer = __esm({
"src/plugins/use-viewer.ts"() {
"use strict";
}
});
// src/app/pylon-handler.ts
import { GraphQLScalarType, Kind as Kind3 } from "graphql";
import {
DateTimeISOResolver,
GraphQLVoid,
JSONObjectResolver,
JSONResolver
} from "graphql-scalars";
import { createSchema, createYoga } from "graphql-yoga";
import { useDisableIntrospection } from "@graphql-yoga/plugin-disable-introspection";
import { readFileSync } from "fs";
import path from "path";
var resolveLazyObject, loadPluginsMiddleware, executeConfig, handler;
var init_pylon_handler = __esm({
"src/app/pylon-handler.ts"() {
"use strict";
init_app();
init_define_pylon();
init_use_sentry();
init_use_unhandled_route();
init_use_viewer();
resolveLazyObject = (obj) => {
return typeof obj === "function" ? obj() : obj;
};
loadPluginsMiddleware = async (plugins) => {
for (const plugin of plugins) {
await plugin.setup?.(app);
if (plugin.middleware) {
pluginsMiddleware.push(plugin.middleware);
}
}
};
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;
};
handler = (options) => {
let {
typeDefs,
resolvers,
graphql: graphql$
} = options;
const graphql = resolveLazyObject(graphql$);
const config = app.config;
if (!typeDefs) {
const schemaPath = path.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 = path.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: new GraphQLScalarType({
name: "Date",
description: "Date represented as an ISO-8601 string",
serialize: DateTimeISOResolver.serialize,
parseValue: DateTimeISOResolver.parseValue,
parseLiteral: DateTimeISOResolver.parseLiteral
}),
JSON: JSONResolver,
JSONObject: 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 === Kind3.INT || ast.kind === Kind3.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();
}
const version2 = globalThis.__PYLON_VERSION__;
if (version2) {
c.header("X-Pylon-Version", version2);
}
return c.newResponse(response.body, response);
};
return handler2;
};
}
});
// 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;
}
var init_create_decorator = __esm({
"src/create-decorator.ts"() {
"use strict";
}
});
// 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) {
}
}
}
var init_get_env = __esm({
"src/get-env.ts"() {
"use strict";
init_context();
}
});
// src/plugins/use-auth/import-private-key.ts
import * as crypto 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;
}
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 crypto.subtle.importKey(
"pkcs8",
binaryDer,
{
name: "RSASSA-PKCS1-v1_5",
hash: "SHA-256"
},
true,
["sign"]
);
}
var convertPKCS1ToPKCS8, importPrivateKey;
var init_import_private_key = __esm({
"src/plugins/use-auth/import-private-key.ts"() {
"use strict";
convertPKCS1ToPKCS8 = (pkcs1) => {
const key = crypto.createPrivateKey(pkcs1);
return key.export({
type: "pkcs8",
format: "pem"
});
};
importPrivateKey = async (pkcs1Pem) => {
const pkcs8Pem = convertPKCS1ToPKCS8(pkcs1Pem);
return await importPKCS8PrivateKey(pkcs8Pem);
};
}
});
// 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 path2 from "path";
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);
}
});
}
};
}
var loadAuthKey, openidConfigCache, bootstrapAuth, PylonAuthException;
var init_use_auth = __esm({
"src/plugins/use-auth/use-auth.ts"() {
"use strict";
init_src();
init_import_private_key();
loadAuthKey = async (keyPath) => {
const authKeyFilePath = path2.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");
}
};
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;
};
PylonAuthException = class extends HTTPException {
// Same constructor as HTTPException
constructor(...args) {
args[1] = {
...args[1],
message: `PylonAuthException: ${args[1]?.message}`
};
super(...args);
}
};
}
});
// src/plugins/use-auth/auth-require.ts
import { env as env2 } from "hono/adapter";
import { HTTPException as HTTPException2 } from "hono/http-exception";
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);
});
}
var authMiddleware;
var init_auth_require = __esm({
"src/plugins/use-auth/auth-require.ts"() {
"use strict";
init_define_pylon();
init_context();
init_create_decorator();
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;
};
}
});
// src/plugins/use-auth/index.ts
var init_use_auth2 = __esm({
"src/plugins/use-auth/index.ts"() {
"use strict";
init_use_auth();
init_auth_require();
}
});
// src/lib/utils.ts
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
function cn(...inputs) {
return twMerge(clsx(inputs));
}
var init_utils = __esm({
"src/lib/utils.ts"() {
"use strict";
}
});
// src/components/logo.tsx
import { jsx, jsxs } from "react/jsx-runtime";
var Logo, logo_default;
var init_logo = __esm({
"src/components/logo.tsx"() {
"use strict";
init_utils();
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"
}
)
]
}
);
};
logo_default = Logo;
}
});
// src/components/global-error-page.tsx
import { useEffect } from "react";
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();
};
const manifest = globalThis.__PYLON_MANIFEST__;
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" }),
manifest?.["index.css"] && /* @__PURE__ */ jsx2(
"link",
{
rel: "stylesheet",
href: manifest["index.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 })
] })
] })
] }) }) })
] });
}
var init_global_error_page = __esm({
"src/components/global-error-page.tsx"() {
"use strict";
init_logo();
}
});
// 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";
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
}
);
}
var buttonVariants;
var init_button = __esm({
"src/components/ui/button.tsx"() {
"use strict";
init_utils();
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"
}
}
);
}
});
// src/components/status-page.tsx
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
var StatusPage;
var init_status_page = __esm({
"src/components/status-page.tsx"() {
"use strict";
init_button();
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 }) })
] });
const manifest = globalThis.__PYLON_MANIFEST__;
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" }),
manifest?.["index.css"] && /* @__PURE__ */ jsx4(
"link",
{
rel: "stylesheet",
href: manifest["index.css"],
precedence: "high"
}
)
] }),
/* @__PURE__ */ jsx4("body", { children: element })
] });
}
return element;
};
}
});
// src/plugins/use-pages/setup/serve-file-path.ts
import { createReadStream } from "fs";
import { stat } from "fs/promises";
import mime from "mime";
import { Readable } from "stream";
var serveFilePath;
var init_serve_file_path = __esm({
"src/plugins/use-pages/setup/serve-file-path.ts"() {
"use strict";
serveFilePath = async ({
filePath,
context
}) => {
let fileStat;
try {
fileStat = await stat(filePath);
} catch (error) {
return context.notFound();
}
const lastModified = fileStat.mtime.toUTCString();
const ifModifiedSince = context.req.header("If-Modified-Since");
if (ifModifiedSince === lastModified) {
return context.body(null, 304);
}
context.header("Last-Modified", lastModified);
const contentType = mime.getType(filePath) || "application/octet-stream";
context.header("Content-Type", contentType);
context.header("Accept-Ranges", "bytes");
const contentLength = fileStat.size;
if (context.req.method === "HEAD") {
context.header("Content-Length", contentLength.toString());
return context.body(null, 200);
}
let start = 0;
let end = contentLength - 1;
let isPartial = false;
const range = context.req.header("Range");
if (range && range.startsWith("bytes=")) {
const parts = range.replace("bytes=", "").split("-");
const rangeStart = parts[0] ? parseInt(parts[0], 10) : 0;
const rangeEnd = parts[1] ? parseInt(parts[1], 10) : contentLength - 1;
if (!isNaN(rangeStart) && !isNaN(rangeEnd) && rangeStart <= rangeEnd) {
start = rangeStart;
end = rangeEnd;
isPartial = true;
}
}
const retrievedLength = end - start + 1;
context.status(isPartial ? 206 : 200);
context.header("Content-Length", retrievedLength.toString());
if (isPartial) {
context.header("Content-Range", `bytes ${start}-${end}/${contentLength}`);
}
const stream = createReadStream(filePath, { start, end });
const webStream = Readable.toWeb(stream);
return context.body(webStream);
};
}
});
// src/plugins/use-pages/setup/index.tsx
var setup_exports = {};
__export(setup_exports, {
setup: () => setup
});
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";
import { etag } from "hono/etag";
import { tmpdir } from "os";
import { pipeline } from "stream/promises";
import { __PYLON_INTERNALS_DO_NOT_USE } from "@getcronit/pylon/pages";
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
import { createHash } from "crypto";
import glob from "tiny-glob/sync.js";
import { jsx as jsx5 } from "react/jsx-runtime";
function escapeXml(unsafe) {
return unsafe.replace(/[<>&'"]/g, (c) => {
switch (c) {
case "<":
return "<";
case ">":
return ">";
case "&":
return "&";
case "'":
return "'";
case '"':
return """;
}
return c;
});
}
function renderSitemapXml(items, baseUrl) {
let xml = `<?xml version="1.0" encoding="UTF-8"?>
`;
xml += `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
`;
for (const item of items) {
const isAbsolute = item.url.startsWith("http://") || item.url.startsWith("https://");
const loc = isAbsolute ? item.url : `${baseUrl}/${item.url.replace(/^\//, "")}`;
xml += ` <url>
`;
xml += ` <loc>${escapeXml(loc)}</loc>
`;
if (item.lastmod) {
const date = item.lastmod instanceof Date ? item.lastmod.toISOString().split("T")[0] : item.lastmod;
xml += ` <lastmod>${escapeXml(String(date))}</lastmod>
`;
}
if (item.changefreq) {
xml += ` <changefreq>${escapeXml(item.changefreq)}</changefreq>
`;
}
if (item.priority !== void 0) {
xml += ` <priority>${item.priority}</priority>
`;
}
xml += ` </url>
`;
}
xml += `</urlset>`;
return xml;
}
function renderSitemapIndexXml(items) {
let xml = `<?xml version="1.0" encoding="UTF-8"?>
`;
xml += `<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
`;
for (const item of items) {
xml += ` <sitemap>
`;
xml += ` <loc>${escapeXml(item.url)}</loc>
`;
if (item.lastmod) {
const date = item.lastmod instanceof Date ? item.lastmod.toISOString().split("T")[0] : item.lastmod;
xml += ` <lastmod>${escapeXml(String(date))}</lastmod>
`;
}
xml += ` </sitemap>
`;
}
xml += `</sitemapindex>`;
return xml;
}
var setup, IMAGE_CACHE_DIR, IS_IMAGE_CACHE_POSSIBLE, getCachedImagePath, calculateDimensions, getContentType, downloadImage;
var init_setup = __esm({
async "src/plugins/use-pages/setup/index.tsx"() {
"use strict";
init_src();
init_global_error_page();
init_status_page();
init_serve_file_path();
setup = async (app2) => {
const pagesManifestPath = path3.join(
process.cwd(),
".pylon/__pylon/pages/manifest.json"
);
const staticManifestPath = path3.join(
process.cwd(),
".pylon/__pylon/static/manifest.json"
);
let pagesManifest = {};
let staticManifest = {};
try {
pagesManifest = JSON.parse(
await fs2.promises.readFile(pagesManifestPath, "utf8")
);
} catch (err) {
throw new Error("Failed to read pages manifest.json:", err);
}
try {
staticManifest = JSON.parse(
await fs2.promises.readFile(staticManifestPath, "utf8")
);
globalThis.__PYLON_MANIFEST__ = staticManifest;
if (pagesManifest["version"]) {
;
globalThis.__PYLON_VERSION__ = pagesManifest["version"];
}
} catch (err) {
throw new Error("Failed to read static manifest.json:", err);
}
const routes = (await import(`${process.cwd()}/${pagesManifest["app.js"]}`)).default;
const _client = await import(`${process.cwd()}/.pylon/client/index.js`);
const 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) {
}
const sitemapCache = /* @__PURE__ */ new Map();
if (pagesManifest["sitemap.js"]) {
try {
const sitemapModule = await import(`${process.cwd()}/${pagesManifest["sitemap.js"]}`);
app2.get("/sitemap.xml", async (c) => {
const cacheKey = "sitemap.xml";
const cached = sitemapCache.get(cacheKey);
const now = Date.now();
const baseUrl = new URL(c.req.url);
const revalidate = sitemapModule.revalidate;
if (revalidate === false || revalidate === 0) {
c.header(
"Cache-Control",
"no-store, no-cache, must-revalidate, proxy-revalidate"
);
} else if (typeof revalidate === "number") {
c.header(
"Cache-Control",
`public, max-age=${revalidate}, s-maxage=${revalidate}, stale-while-revalidate`
);
}
if (cached && cached.expiresAt > now) {
c.header("Content-Type", "application/xml");
return c.body(cached.xml);
}
let xml = "";
if (sitemapModule.generateSitemaps) {
const sitemaps = await sitemapModule.generateSitemaps();
const indexItems = sitemaps.map((s) => ({
url: `${baseUrl.origin}/sitemap/${s.id}.xml`
}));
xml = renderSitemapIndexXml(indexItems);
} else {
const sitemapFn = sitemapModule.sitemap || sitemapModule.default;
if (sitemapFn) {
const items = await sitemapFn();
xml = renderSitemapXml(items, baseUrl.origin);
} else {
return c.text("Sitemap not found", 404);
}
}
if (typeof revalidate === "number" && revalidate > 0) {
sitemapCache.set(cacheKey, {
xml,
expiresAt: now + revalidate * 1e3
});
}
c.header("Content-Type", "application/xml");
return c.body(xml);
});
app2.get("/sitemap/:id", async (c) => {
const idParam = c.req.param("id");
if (!idParam.endsWith(".xml")) {
return c.text("Sitemap not found", 404);
}
const id = idParam.replace(".xml", "");
const cacheKey = `sitemap-${id}.xml`;
const cached = sitemapCache.get(cacheKey);
const now = Date.now();
const baseUrl = new URL(c.req.url);
const revalidate = sitemapModule.revalidate;
if (revalidate === false || revalidate === 0) {
c.header(
"Cache-Control",
"no-store, no-cache, must-revalidate, proxy-revalidate"
);
} else if (typeof revalidate === "number") {
c.header(
"Cache-Control",
`public, max-age=${revalidate}, s-maxage=${revalidate}, stale-while-revalidate`
);
}
if (cached && cached.expiresAt > now) {
c.header("Content-Type", "application/xml");
return c.body(cached.xml);
}
let xml = "";
const sitemapFn = sitemapModule.sitemap || sitemapModule.default;
if (sitemapFn) {
const items = await sitemapFn({ id });
xml = renderSitemapXml(items, baseUrl.origin);
} else {
return c.text("Sitemap not found", 404);
}
if (typeof revalidate === "number" && revalidate > 0) {
sitemapCache.set(cacheKey, {
xml,
expiresAt: now + revalidate * 1e3
});
}
c.header("Content-Type", "application/xml");
return c.body(xml);
});
} catch (e) {
console.error("Failed to load sitemap module:", e);
}
}
app2.on(
"GET",
publicFiles.map((file) => `/${file}`),
etag(),
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/*", etag(), 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);
}
});
const requestStore = new AsyncLocalStorage3();
app2.get("*", async (c) => {
const initCtx = requestStore.getStore() || {
client: null
};
return requestStore.run(initCtx, async () => {
if (!initCtx.client) {
initCtx.client = _client.pageClient();
}
const client = initCtx.client;
const staticHandlerContext = await handler2.query(c.req.raw);
if (staticHandlerContext instanceof Response) {
return staticHandlerContext;
}
const xPylonRouteRef = c.req.header("x-pylon-route-ref");
const router = createStaticRouter(
handler2.dataRoutes,
staticHandlerContext
);
const component = /* @__PURE__ */ jsx5(__PYLON_INTERNALS_DO_NOT_USE.DataClientProvider, { client, children: /* @__PURE__ */ jsx5(
__PYLON_INTERNALS_DO_NOT_USE.SSRPruningProvider,
{
target: xPylonRouteRef || null,
children: /* @__PURE__ */ jsx5(
StaticRouterProvider,
{
router,
context: staticHandlerContext
}
)
}
) });
if (c.req.header("accept")?.includes("application/json")) {
let cacheSnapshot;
try {
const data = await client.prepareReactRender(component);
cacheSnapshot = data.cacheSnapshot;
} catch (error) {
if (error instanceof Response) {
return error;
}
}
const context = c.get("pagesContext") || {};
return c.json({
cacheSnapshot,
context,
version: pagesManifest["version"]
});
}
try {
if (reactServer.renderToReadableStream) {
try {
const stream = await reactServer.renderToReadableStream(component, {
bootstrapModules: staticManifest["app.js"] ? [staticManifest["app.js"]] : void 0
});
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: staticManifest["app.js"] ? [staticManifest["app.js"]] : void 0,
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 })
)
);
}
});
});
};
IMAGE_CACHE_DIR = path3.join(process.cwd(), ".cache/__pylon/images");
IS_IMAGE_CACHE_POSSIBLE = true;
try {
await fs2.promises.mkdir(IMAGE_CACHE_DIR, { recursive: true });
} catch (error) {
IS_IMAGE_CACHE_POSSIBLE = false;
}
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);
};
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 };
};
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";
}
};
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/app-utils.ts
import fs3 from "fs";
import path4 from "path";
function formatSegment(segment) {
let sanitized = segment;
if (sanitized.startsWith("[...") && sanitized.endsWith("]")) {
const param = sanitized.slice(4, -1);
sanitized = "CatchAll" + param.charAt(0).toUpperCase() + param.slice(1);
} else if (sanitized.startsWith("[") && sanitized.endsWith("]")) {
sanitized = sanitized.slice(1, -1);
}
return sanitized.charAt(0).toUpperCase() + sanitized.slice(1);
}
function getLayoutComponentName(filePath) {
const segments = filePath.replace(PAGES_DIR, "").replace(/\\/g, "/").replace(/layout\.tsx$/, "").split("/").filter(Boolean);
return segments.map(formatSegment).join("") + "Layout";
}
function getPageComponentName(filePath) {
const segments = filePath.replace(PAGES_DIR, "").replace(/\\/g, "/").replace(/page\.tsx$/, "").split("/").filter(Boolean);
return segments.map(formatSegment).join("") + "Page";
}
function convertToDynamicRoute(segment) {
if (segment.startsWith("[...") && segment.endsWith("]")) return "*";
if (segment.startsWith("[") && segment.endsWith("]"))
return `:${segment.slice(1, -1)}`;
return segment;
}
function processLayoutItem(relativePath, importPath, route, context) {
const layoutComponentName = getLayoutComponentName(relativePath);
context.imports.push(`import ${layoutComponentName} from ${importPath};`);
const componentName = layoutComponentName === "Layout" ? `RootLayout` : `${layoutComponentName}`;
const catchAllParam = relativePath.match(/\[\.\.\.(.+)\]/)?.[1];
const paramMatches = [...relativePath.matchAll(/\[(.+?)\]/g)].map(
(m) => m[1].replace("...", "")
);
route.Component = `withLoaderData((props) => <${componentName} children={<Outlet />} {...props} />, "${componentName}", ${catchAllParam ? `"${catchAllParam}"` : "undefined"})`;
route.loader = `loader("${componentName}")`;
route.shouldRevalidate = `({ currentParams, nextParams, formData, defaultShouldRevalidate }) => {
// Revalidate if a form was submitted (standard behavior)
if (formData) return true;
// List of params this layout segment depends on
const relevantKeys = ${JSON.stringify(paramMatches)};
// Check if any relevant URL parameter changed
const hasParamChanged = relevantKeys.some(key =>
JSON.stringify(currentParams[key]) !== JSON.stringify(nextParams[key])
);
// If it's the RootLayout, we might only want to revalidate on hard refreshes
// or specific global triggers. Otherwise, follow param changes.
return hasParamChanged || (relevantKeys.length === 0 && defaultShouldRevalidate);
}`;
if (route.path === "/") {
route.errorElement = "<ErrorElement standalone={true} />";
}
route.HydrateFallback = "HydrateFallback";
}
function processPageItem(relativePath, importPath, route) {
const catchAllParam = relativePath.match(/\[\.\.\.(.+)\]/)?.[1];
const pageComponentName = getPageComponentName(relativePath);
route.children.push({
path: void 0,
index: true,
errorElement: "<ErrorElement standalone={false} />",
lazy: `async () => {const i = await import(${importPath}).catch(() => {window.location.reload()}); return {Component: withLoaderData(i.default, "${pageComponentName}", ${catchAllParam ? `"${catchAllParam}"` : "undefined"})}}`,
HydrateFallback: "HydrateFallback",
loader: `loader("${pageComponentName}")`
});
}
function optimizeRouteStructure(route, hasLayout) {
if (!hasLayout && route.children?.length === 1 && route.children[0].path === "*") {
const child = route.children[0];
const currentPath = route.path === "/" ? "" : route.path;
Object.assign(route, child);
route.path = currentPath ? `${currentPath}/*` : "*";
delete route.children;
}
if (route.path === "*" && !hasLayout && route.children?.length === 1) {
const child = route.children[0];
if (child.index || child.path === "*") {
const currentPath = route.path;
Object.assign(route, child);
route.path = currentPath;
delete route.index;
delete route.children;
}
}
if (route.path === "*" && hasLayout && route.children) {
const pageChild = route.children.find((child) => child.index);
if (pageChild) {
delete pageChild.index;
pageChild.path = "*";
}
}
}
function scanDirectory(directory, context, 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, context, relativePath);
if (childRoute) {
route.children.push(childRoute);
}
} else if (item.name === "layout.tsx") {
processLayoutItem(relativePath, importPath, route, context);
hasLayout = true;
} else if (item.name === "page.tsx") {
processPageItem(relativePath, importPath, route);
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) {
context.routeSlugs.push(fullPath);
}
}
if (hasLayout) {
const childNotFoundRoute = {
path: "*",
element: "<NotFoundPage standalone={false} />"
};
if (!route.children) {
route.children = [];
}
route.children.push(childNotFoundRoute);
}
optimizeRouteStructure(route, hasLayout);
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 generateRouteFileContent(context, rootRoute, notFoundRoute) {
return `${context.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 }>, name?: string, catchAllParam?: string) {
return function WithLoaderDataWrapper(props: T) {
const dataClient = __PYLON_INTERNALS_DO_NOT_USE.useDataClient()
const pruningTarget = __PYLON_INTERNALS_DO_NOT_USE.useSSRPruning()
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 = useMemo(() => Object.fromEntries(searchParams.entries()), [searchParams])
const reactRouterParams = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.useParams()
const params = useMemo(() => {
const params: Record<string, string | string[] | undefined> = reactRouterParams
if (catchAllParam && reactRouterParams['*']) {
params[catchAllParam] = reactRouterParams['*']?.split('/')
}
return params
}, [reactRouterParams, catchAllParam])
// 1. Handle Transparent Ancestors
// If we're optimized-rendering a specific layout, and THIS is not it,
// we just act as a passthrough to skip THIS layout's logic/queries.
// Exception: RootLayout is never skipped to preserve global providers.
if (pruningTarget && name !== pruningTarget && name !== 'RootLayout') {
return <Outlet />
}
const {useQuery, useHydrateCache} = useMemo(() => dataClient.pageClient(), [])
if(cacheSnapshot) {
useHydrateCache({cacheSnapshot})
}
const data = typeof window !== "undefined" ? useQuery() : dataClient.useQuery()
const pageProps = useMemo(() => {
return {
path: location.pathname,
params,
searchParams: searchParamsObject,
data,
context,
}
}, [location.pathname, params, searchParamsObject, data, context])
// 2. Handle Pruning Target
// If THIS is the target, we render it but clear its children (the Outlet).
const children = pruningTarget && name === pruningTarget ? null : <Outlet />
return <__PYLON_INTERNALS_DO_NOT_USE.RouteDataProvider props={pageProps} name={name}>
<Component {...(props as any)} {...pageProps} children={children} />
</__PYLON_INTERNALS_DO_NOT_USE.RouteDataProvider>
};
}
const loader: (ref?: string) => __PYLON_ROUTER_INTERNALS_DO_NOT_USE.LoaderFunction = (ref) => async ({ request, ...args }) => {
// 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)
}
// Set Accept-Encoding header to identity so the internal fetch returns JSON
headers.set('Accept-Encoding', 'identity')
} 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
headers.set('X-Pylon-Internal', 'true')
if(ref) {
headers.set('X-Pylon-Route-Ref', ref)
}
const response = await fetchToUse(url.pathname + url.search, {
method: 'GET',
headers,
})
try {
const data = await response.json<any>()
// Check if the version returned by the server matches the client's version
if (data && data.version && typeof window !== 'undefined' && (window as any).__PYLON_VERSION__ && data.version !== (window as any).__PYLON_VERSION__) {
window.location.reload()
}
return data
} catch {
return null
}
}
const RootLayout = (props: { children: React.ReactNode; [key: string]: any }) => {
const manifest = (globalThis as any).__PYLON_MANIFEST__;
return (
<Layout {...props}>
<meta charSet="utf-8" />
{manifest?.['index.css'] && <link rel="stylesheet" href={manifest['index.css']} precedence="high" />}
{manifest?.['app.css'] && <link rel="stylesheet" href={manifest['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
`;
}
function makeAppFiles() {
const context = { imports: [], routeSlugs: [] };
const rootRoute = scanDirectory(PAGES_DIR, context);
const notFoundRoute = {
path: "*",
element: "<NotFoundPage standalone={true} />"
};
const routes = generateRouteFileContent(context, rootRoute, notFoundRoute);
const slugs = `export default ${JSON.stringify(context.routeSlugs, null, 2)}`;
return {
routes,
slugs
};
}
var PAGES_DIR;
var init_app_utils = __esm({
"src/plugins/use-pages/build/app-utils.ts"() {
"use strict";
PAGES_DIR = "./pages";
}
});
// src/plugins/use-pages/build/plugins/external-esm-plugin.ts
import escapeStringRegexp from "escape-string-regexp";
function makeFilter(externals) {
return new RegExp(
"^(" + externals.map(escapeStringRegexp).join("|") + ")(\\/.*)?$"
// TODO support for query strings?
);
}
var NAME, NAMESPACE, esmExternalsPlugin;
var init_external_esm_plugin = __esm({
"src/plugins/use-pages/build/plugins/external-esm-plugin.ts"() {
"use strict";
NAME = "esm-externals";
NAMESPACE = NAME;
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/plugins/image-plugin.ts
import { createHash as createHash2 } from "crypto";
import path5 from "path";
import fs4 from "fs/promises";
var imagePlugin;
var init_image_plugin = __esm({
"src/plugins/use-pages/build/plugins/image-plugin.ts"() {
"use strict";
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 = path5.resolve(args.resolveDir, args.path);
const fileName = path5.basename(filePath);
const extname = path5.extname(filePath);
const hash = createHash2("md5").update(filePath + await fs4.readFile(filePath)).digest("hex").slice(0, 8);
const newFilename = `${fileName}-${hash}${extname}`;
const newFilePath = path5.join(outdir, "media", newFilename);
await fs4.mkdir(path5.dirname(newFilePath), { recursive: true });
await fs4.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/${path5.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/inject-app-hydration.ts
import fs5 from "fs/promises";
import path6 from "path";
var injectAppHydrationPlugin;
var init_inject_app_hydration = __esm({
"src/plugins/use-pages/build/plugins/inject-app-hydration.ts"() {
"use strict";
injectAppHydrationPlugin = (version2) => ({
name: "inject-hydration",
setup(build2) {
build2.onLoad({ filter: /.*/, namespace: "file" }, async (args) => {
if (args.path === path6.resolve(process.cwd(), ".pylon", "app.tsx")) {
let contents = await fs5.readFile(args.path, "utf-8");
const clientPath = path6.resolve(process.cwd(), ".pylon/client");
const pathToClient = path6.relative(path6.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'
// @ts-ignore
window.__PYLON_VERSION__ = "${version2}"
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/postcss-plugin.ts
import fs6 from "fs/promises";
import loadConfig from "postcss-load-config";
import postcss from "postcss";
var postcssPlugin;
var init_postcss_plugin = __esm({
"src/plugins/use-pages/build/plugins/postcss-plugin.ts"() {
"use strict";
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/index.ts
var build_exports = {};
__export(build_exports, {
build: () => build
});
import chokidar from "chokidar";
import esbuild from "esbuild";
import fs7 from "fs/promises";
import path7 from "path";
async function updateFileIfChanged(filePath, newContent) {
await fs7.mkdir(path7.dirname(filePath), { recursive: true });
try {
const currentContent = await fs7.readFile(filePath);
if (currentContent.equals(newContent)) {
return false;
}
} catch (err) {
if (err.code !== "ENOENT") throw err;
}
await fs7.writeFile(filePath, newContent);
return true;
}
var DIST_STATIC_DIR, DIST_PAGES_DIR, build;
var init_build = __esm({
"src/plugins/use-pages/build/index.ts"() {
"use strict";
init_app_utils();
init_external_esm_plugin();
init_image_plugin();
init_inject_app_hydration();
init_postcss_plugin();
DIST_STATIC_DIR = path7.join(process.cwd(), ".pylon/__pylon/static");
DIST_PAGES_DIR = path7.join(process.cwd(), ".pylon/__pylon/pages");
build = async ({ onBuild }) => {
const version2 = Math.random().toString(36).substring(7);
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, force: true });
} catch (err) {
if (err.code !== "ENOENT") throw err;
}
};
const pylonCssPath = path7.join(
process.cwd(),
"node_modules",
"@getcronit/pylon/dist/pages/index.css"
);
const buildAppFilePlugin = {
name: "build-app-file",
setup(build2) {
build2.onStart(async () => {
await buildAppFile();
});
}
};
const writeOnEndPlugin = {
name: "write-on-end",
setup(build2) {
build2.initialOptions.metafile = true;
build2.initialOptions.write = false;
build2.onEnd(async (result) => {
const manifest = {};
for (const [key, value] of Object.entries(
result.metafile?.outputs || {}
)) {
if (value.entryPoint === ".pylon/app.tsx") {
manifest["app.js"] = key;
if (value.cssBundle) {
manifest["app.css"] = value.cssBundle;
}
} else if (value.entryPoint?.endsWith("pylon/dist/pages/index.css")) {
manifest["index.css"] = key;
} else if (value.entryPoint?.endsWith("pages/sitemap.ts")) {
manifest["sitemap.js"] = key;
}
}
if (build2.initialOptions.publicPath) {
const publicPath = build2.initialOptions.publicPath;
for (const [key, value] of Object.entries(manifest)) {
const index = value.indexOf(publicPath);
if (index !== -1) {
manifest[key] = value.slice(index);
}
}
}
manifest["version"] = version2;
await updateFileIfChanged(
path7.join(build2.initialOptions.outdir, "manifest.json"),
Buffer.from(JSON.stringify(manifest, null, 2))
);
await Promise.all(
result.outputFiles.map(async (file) => {
await fs7.mkdir(path7.dirname(file.path), { recursive: true });
await updateFileIfChanged(file.path, file.contents);
})
);
if (result.errors.length === 0) {
onBuild();
}
});
}
};
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 sitemapExists = await fs7.access(path7.join(process.cwd(), "pages/sitemap.ts")).then(() => true).catch(() => false);
const clientCtx = await esbuild.context({
sourcemap: "linked",
write: false,
metafile: true,
nodePaths,
absWorkingDir: process.cwd(),
plugins: [
buildAppFilePlugin,
injectAppHydrationPlugin(version2),
imagePlugin,
postcssPlugin,
writeOnEndPlugin,
timePlugin("client")
],
publicPath: "/__pylon/static",
assetNames: "assets/[name]-[hash]",
chunkNames: "chunks/[name]-[hash]",
entryNames: "./[name]-[hash]",
format: "esm",
platform: "browser",
entryPoints: [".pylon/app.tsx", pylonCssPath],
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,
metafile: true,
absWorkingDir: process.cwd(),
nodePaths,
plugins: [
buildAppFilePlugin,
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]",
entryNames: "./[name]-[hash]",
format: "esm",
platform: "node",
entryPoints: [
".pylon/app.tsx",
pylonCssPath,
...sitemapExists ? ["./pages/sitemap.ts"] : []
],
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();
pagesWatcher = chokidar.watch("pages", { ignoreInitial: true });
pagesWatcher.on("all", async (event, path8) => {
if (["add", "change", "unlink"].includes(event)) {
await copyPublicDir();
}
});
await Promise.all([clientCtx.watch(), serverCtx.watch()]);
},
dispose: async () => {
if (pagesWatcher) {
pagesWatcher.close();
}
Promise.all([clientCtx.dispose(), serverCtx.dispose()]);
},
rebuild: async () => {
await copyPublicDir();
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",
// We use async functions here so React isn't imported until setup() is called
setup: async (api) => {
const { setup: setup2 } = await init_setup().then(() => setup_exports);
return setup2(api);
},
build: async (api) => {
const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
return build2(api);
}
};
}
var init_use_pages = __esm({
"src/plugins/use-pages/index.ts"() {
"use strict";
}
});
// src/gateway.ts
import { getContext as getContext2, getResolveInfo as getResolveInfo2 } from "@getcronit/pylon";
import { delegateToSchema } from "@graphql-tools/delegate";
import { buildHTTPExecutor } from "@graphql-tools/executor-http";
import { schemaFromExecutor, wrapSchema } from "@graphql-tools/wrap";
import {
Kind as Kind4,
OperationTypeNode,
visit
} from "graphql";
function astFromJSValue(value) {
if (typeof value === "string")
return { kind: Kind4.STRING, value };
if (typeof value === "number")
return { kind: Kind4.INT, value: String(value) };
if (typeof value === "boolean")
return { kind: Kind4.BOOLEAN, value };
return { kind: Kind4.STRING, value: String(value) };
}
function buildSelectionsFromNeeds(needs) {
const selections = [];
for (const [key, value] of Object.entries(needs)) {
if (key === "__args" || !value) continue;
let selectionSet = void 0;
let argsNodes = void 0;
if (typeof value === "object") {
const nestedSelections = buildSelectionsFromNeeds(value);
if (nestedSelections.length > 0) {
selectionSet = {
kind: Kind4.SELECTION_SET,
selections: nestedSelections
};
}
if (value.__args) {
argsNodes = Object.entries(value.__args).map(
([argName, argVal]) => ({
kind: Kind4.ARGUMENT,
name: { kind: Kind4.NAME, value: argName },
value: astFromJSValue(argVal)
})
);
}
}
selections.push({
kind: Kind4.FIELD,
name: { kind: Kind4.NAME, value: key },
...selectionSet ? { selectionSet } : {},
...argsNodes ? { arguments: argsNodes } : {}
});
}
return selections;
}
function createGateway() {
return {
configure: (config) => {
return new PylonGateway(config);
}
};
}
var schemaCache, InjectNeedsTransform, PylonPatchTransform, PylonGateway;
var init_gateway = __esm({
"src/gateway.ts"() {
"use strict";
schemaCache = /* @__PURE__ */ new Map();
InjectNeedsTransform = class {
constructor(needs) {
this.needs = needs;
}
transformRequest(originalRequest) {
if (!this.needs || Object.keys(this.needs).length === 0) {
return originalRequest;
}
const needsSelections = buildSelectionsFromNeeds(this.needs);
let rootFieldFound = false;
const document = visit(originalRequest.document, {
Field(node) {
if (!rootFieldFound && node.selectionSet) {
rootFieldFound = true;
const existingNames = new Set(
node.selectionSet.selections.filter((s) => s.kind === Kind4.FIELD).map((s) => s.name.value)
);
const mergedSelections = [...node.selectionSet.selections];
for (const selection of needsSelections) {
if (selection.kind === Kind4.FIELD && !existingNames.has(selection.name.value)) {
mergedSelections.push(selection);
}
}
return {
...node,
selectionSet: {
...node.selectionSet,
selections: mergedSelections
}
};
}
}
});
return { ...originalRequest, document };
}
};
PylonPatchTransform = class {
constructor(patches, api) {
this.patches = patches;
this.api = api;
}
// Injects __typename into all selection sets via AST traversal to ensure
// deterministic resolution of interface/union types for downstream runtime transformations.
transformRequest(originalRequest) {
const document = visit(originalRequest.document, {
SelectionSet(node) {
const hasTypename = node.selections.some(
(s) => s.kind === Kind4.FIELD && s.name.value === "__typename"
);
if (!hasTypename) {
return {
...node,
selections: [
...node.selections,
{
kind: Kind4.FIELD,
name: { kind: Kind4.NAME, value: "__typename" }
}
]
};
}
}
});
return { ...originalRequest, document };
}
// Intercepts the execution phase to recursively apply registered patches to the payload.
transformResult(originalResult) {
return this.applyTransforms(originalResult);
}
applyTransforms(data) {
if (!data || typeof data !== "object") return data;
if (Array.isArray(data)) return data.map((item) => this.applyTransforms(item));
const processedData = { ...data };
for (const key in processedData) {
processedData[key] = this.applyTransforms(processedData[key]);
}
const typeName = data.__typename;
const patchFn = this.patches[typeName];
if (patchFn) {
const patchedData = patchFn(processedData, this.api);
if (patchedData && typeof patchedData === "object" && !Array.isArray(patchedData)) {
return {
...processedData,
...patchedData
};
}
return patchedData;
}
return processedData;
}
};
PylonGateway = class {
constructor(config) {
this.config = config;
this.apiContext = {
delegate: this.delegate.bind(this)
};
}
apiContext;
async delegate(key, ...opts) {
const { info } = getResolveInfo2();
const ctx = getContext2();
if (!info || !ctx) throw new Error("Pylon context missing");
const options = opts[0];
const args = options?.args || {};
const needs = options?.needs;
const [rootType, fieldName] = String(key).split(".");
if (!rootType || !fieldName) {
throw new Error(
`Invalid delegate key format: ${String(key)}. Expected "Operation.field"`
);
}
const operationMap = {
Query: OperationTypeNode.QUERY,
Mutation: OperationTypeNode.MUTATION,
Subscription: OperationTypeNode.SUBSCRIPTION
};
const operation = operationMap[rootType];
if (!operation) {
throw new Error(
`Unsupported operation type "${rootType}" in key "${String(key)}"`
);
}
if (!schemaCache.has(this.config.url)) {
const executor = buildHTTPExecutor({
endpoint: this.config.url,
headers: (r) => ({
...this.config.headers ? this.config.headers(r?.context) : {}
})
});
const schemaPromise = schemaFromExecutor(executor).then(
(schema2) => wrapSchema({ schema: schema2, executor })
);
schemaCache.set(this.config.url, schemaPromise);
}
const schema = await schemaCache.get(this.config.url);
const result = await delegateToSchema({
schema,
operation,
fieldName,
args,
context: ctx,
info,
transforms: [
new InjectNeedsTransform(needs),
// Injects requested AST fields
new PylonPatchTransform(this.config.patches, this.apiContext)
]
});
return result;
}
};
}
});
// src/index.ts
import { createPubSub } from "graphql-yoga";
var init_src = __esm({
"src/index.ts"() {
init_pylon_handler();
init_context();
init_create_decorator();
init_define_pylon();
init_get_env();
init_use_auth2();
init_use_pages();
init_app();
init_gateway();
}
});
init_src();
export {
ServiceError,
app,
asyncContext,
authMiddleware,
createDecorator,
createGateway,
executeConfig,
createPubSub as experimentalCreatePubSub,
getContext,
getEnv,
getResolveInfo,
handler,
requireAuth,
setContext,
useAuth,
usePages
};
//# sourceMappingURL=index.js.map