@getcronit/pylon
Version:

1,619 lines • 55.7 kB
JavaScript
import {
__require
} from "./chunk-R6NNLVZJ.js";
// src/index.ts
import { createPubSub } from "graphql-yoga";
// 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";
// 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";
// src/context.ts
import { AsyncLocalStorage } from "async_hooks";
import { env } from "hono/adapter";
var asyncContext = new AsyncLocalStorage();
var getContext = () => {
const ctx = asyncContext.getStore();
if (!ctx) {
throw new Error("Context not defined");
}
ctx.env = env(ctx);
return ctx;
};
var setContext = (context) => {
return asyncContext.enterWith(context);
};
// src/app/index.ts
var app = new Hono();
app.use("*", compress());
app.use("*", sentry());
app.use("*", async (c, next) => {
return new Promise((resolve, reject) => {
asyncContext.run(c, async () => {
try {
resolve(await next());
} catch (error) {
reject(error);
}
});
});
});
app.use("*", except(["/__pylon/*"], logger()));
var pluginsMiddleware = [];
var pluginsMiddlewareLoader = async (c, next) => {
for (const middleware of pluginsMiddleware) {
const response = await middleware(c, async () => {
});
if (response) {
return response;
}
}
return next();
};
app.use(pluginsMiddlewareLoader);
// src/define-pylon.ts
import * as Sentry from "@sentry/bun";
import consola from "consola";
import {
getNamedType,
GraphQLError,
GraphQLUnionType,
isInterfaceType,
isNonNullType,
isObjectType,
Kind
} from "graphql";
// src/resolve-info.ts
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
var executionAsyncContext = new AsyncLocalStorage2();
var getResolveInfo = () => {
const store = executionAsyncContext.getStore();
if (!store) {
throw new Error("Resolve info is not available");
}
return store;
};
// src/define-pylon.ts
var uniqueFieldsCache = /* @__PURE__ */ new WeakMap();
var selectionSetCache = /* @__PURE__ */ new WeakMap();
var 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;
};
var 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);
const isPylonResolver = resolver[name] && typeof resolver[name] === "function";
if (hasAliases && !isPylonResolver) {
result[name] = (args, ctx, info) => {
const aliasKey = info.fieldNodes[0].alias?.value;
const schemaKey = info.fieldName;
console.log(aliasKey, schemaKey, resolver, name);
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;
};
var 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;
};
var 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 = isOriginalGraphQLError;
var useSentry = (options = {}) => {
function pick(key, defaultValue) {
return options[key] ?? defaultValue;
}
const startTransaction = pick("startTransaction", true);
const includeRawResult = pick("includeRawResult", false);
const includeExecuteVariables = pick("includeExecuteVariables", false);
const renameTransaction = pick("renameTransaction", false);
const skipOperation = pick("skip", () => false);
const skipError = pick("skipError", defaultSkipError);
const eventIdKey = options.eventIdKey === null ? null : "sentryEventId";
function addEventId(err, eventId) {
if (eventIdKey !== null && eventId !== null) {
err.extensions[eventIdKey] = eventId;
}
return err;
}
return {
onExecute({ args }) {
if (skipOperation(args)) {
return;
}
const rootOperation = args.document.definitions.find(
(o) => o.kind === 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);
}
};
}
};
};
// src/plugins/use-unhandled-route.ts
import { html } from "hono/html";
// package.json
var version = "3.0.0-canary-20260508115545.74ba3bff3f7ade973010148205bb756b3e5642cc";
// src/plugins/use-unhandled-route.ts
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
);
});
}
};
}
// 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>
`
);
});
}
};
}
// src/app/pylon-handler.ts
var resolveLazyObject = (obj) => {
return typeof obj === "function" ? obj() : obj;
};
var loadPluginsMiddleware = async (plugins) => {
for (const plugin of plugins) {
await plugin.setup?.(app);
if (plugin.middleware) {
pluginsMiddleware.push(plugin.middleware);
}
}
};
var executeConfig = async (config, args) => {
const plugins = [useSentry(), useViewer(), ...config?.plugins || []];
if (config?.landingPage ?? true) {
plugins.push(useUnhandledRoute());
}
if (config?.graphiql === false) {
plugins.push(useDisableIntrospection());
}
const pluginsStrategy = args?.pluginsStrategy || "first";
await loadPluginsMiddleware(
plugins.filter((p) => {
if (!p.strategy) {
p.strategy = "first";
}
return p.strategy === pluginsStrategy;
})
);
config.plugins = plugins;
app.config = config;
};
var handler = (options) => {
let {
typeDefs,
resolvers,
graphql: graphql$
} = options;
const graphql = resolveLazyObject(graphql$);
const config = app.config;
if (!typeDefs) {
const schemaPath = 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;
}
// src/get-env.ts
function getEnv() {
const start = Date.now();
const skipTracing = arguments[0] === true;
try {
const context = asyncContext.getStore();
const ctx = context.env || process.env || {};
ctx.NODE_ENV = ctx.NODE_ENV || process.env.NODE_ENV || "development";
return ctx;
} catch {
return process.env;
} finally {
if (!skipTracing) {
}
}
}
// src/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";
// 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;
}
var convertPKCS1ToPKCS8 = (pkcs1) => {
const key = crypto.createPrivateKey(pkcs1);
return key.export({
type: "pkcs8",
format: "pem"
});
};
function importPKCS8PrivateKey(pem) {
const pemHeader = "-----BEGIN PRIVATE KEY-----";
const pemFooter = "-----END PRIVATE KEY-----";
const pemContents = pem.substring(
pemHeader.length,
pem.length - pemFooter.length - 1
);
const binaryDerString = atob(pemContents);
const binaryDer = str2ab(binaryDerString);
return crypto.subtle.importKey(
"pkcs8",
binaryDer,
{
name: "RSASSA-PKCS1-v1_5",
hash: "SHA-256"
},
true,
["sign"]
);
}
var importPrivateKey = async (pkcs1Pem) => {
const pkcs8Pem = convertPKCS1ToPKCS8(pkcs1Pem);
return await importPKCS8PrivateKey(pkcs8Pem);
};
// src/plugins/use-auth/use-auth.ts
var loadAuthKey = async (keyPath) => {
const authKeyFilePath = 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");
}
};
var openidConfigCache;
var bootstrapAuth = async (issuer, keyPath) => {
if (!openidConfigCache) {
const authKey = await loadAuthKey(keyPath);
openidConfigCache = await openid.discovery(
new URL(issuer),
authKey.clientId,
void 0,
openid.PrivateKeyJwt({
key: await importPrivateKey(authKey.key),
kid: authKey.keyId
})
);
}
return openidConfigCache;
};
var PylonAuthException = class extends HTTPException {
// Same constructor as HTTPException
constructor(...args) {
args[1] = {
...args[1],
message: `PylonAuthException: ${args[1]?.message}`
};
super(...args);
}
};
function useAuth(args) {
const { issuer, endpoint = "/auth", keyPath = "key.json" } = args;
const loginPath = `${endpoint}/login`;
const logoutPath = `${endpoint}/logout`;
const callbackPath = `${endpoint}/callback`;
return {
middleware: async (ctx, next) => {
const openidConfig = await bootstrapAuth(issuer, keyPath);
ctx.set("auth", { openidConfig });
const authCookieToken = getCookie(ctx, "pylon-auth");
const authHeader = ctx.req.header("Authorization");
const authQueryToken = ctx.req.query("token");
if (authCookieToken || authHeader || authQueryToken) {
let token;
if (authHeader) {
const [type, value] = authHeader.split(" ");
if (type === "Bearer") {
token = value;
}
} else if (authQueryToken) {
token = authQueryToken;
} else if (authCookieToken) {
token = authCookieToken;
}
if (!token) {
throw new PylonAuthException(401, {
message: "Invalid token"
});
}
const introspection = await openid.tokenIntrospection(
openidConfig,
token,
{
scope: "openid email profile"
}
);
if (!introspection.active) {
throw new PylonAuthException(401, {
message: "Token is not active"
});
}
if (!introspection.sub) {
throw new PylonAuthException(401, {
message: "Token is missing subject"
});
}
const userInfo = await openid.fetchUserInfo(
openidConfig,
token,
introspection.sub
);
const roles = Object.keys(
introspection["urn:zitadel:iam:org:projects:roles"]?.valueOf() || {}
);
ctx.set("auth", {
user: {
...userInfo,
roles
},
openidConfig
});
return next();
}
},
setup(app2) {
app2.get(loginPath, async (ctx) => {
const openidConfig = ctx.get("auth").openidConfig;
const codeVerifier = openid.randomPKCECodeVerifier();
const codeChallenge = await openid.calculatePKCECodeChallenge(
codeVerifier
);
setCookie(ctx, "pylon_code_verifier", codeVerifier, {
httpOnly: true,
maxAge: 300
// 5 minutes
});
let scope = "openid profile email urn:zitadel:iam:user:resourceowner urn:zitadel:iam:org:projects:roles";
const parameters = {
scope,
code_challenge: codeChallenge,
code_challenge_method: "S256",
redirect_uri: new URL(ctx.req.url).origin + "/auth/callback",
state: openid.randomState()
};
const authorizationUrl = openid.buildAuthorizationUrl(
openidConfig,
parameters
);
return ctx.redirect(authorizationUrl);
});
app2.get(logoutPath, async (ctx) => {
deleteCookie(ctx, "pylon-auth");
return ctx.redirect("/");
});
app2.get(callbackPath, async (ctx) => {
const openidConfig = ctx.get("auth").openidConfig;
const params = ctx.req.query();
const code = params.code;
const state = params.state;
if (!code || !state) {
throw new PylonAuthException(400, {
message: "Missing authorization code or state"
});
}
const codeVerifier = getCookie(ctx, "pylon_code_verifier");
if (!codeVerifier) {
throw new PylonAuthException(400, {
message: "Missing code verifier"
});
}
try {
const cbUrl = new URL(ctx.req.url);
let tokenSet = await openid.authorizationCodeGrant(
openidConfig,
cbUrl,
{
pkceCodeVerifier: codeVerifier,
expectedState: state
},
cbUrl.searchParams
);
setCookie(ctx, `pylon-auth`, tokenSet.access_token, {
httpOnly: true,
maxAge: tokenSet.expires_in || 3600
// Default to 1 hour if not specified
});
return ctx.redirect("/");
} catch (error) {
console.error("Error during token exchange:", error);
return ctx.text("Authentication failed!", 500);
}
});
}
};
}
// src/plugins/use-auth/auth-require.ts
import { env as env2 } from "hono/adapter";
import { HTTPException as HTTPException2 } from "hono/http-exception";
var authMiddleware = (checks = {}) => {
const middleware = async (ctx, next) => {
const AUTH_PROJECT_ID = env2(ctx).AUTH_PROJECT_ID;
const auth = ctx.get("auth");
if (!auth) {
throw new HTTPException2(401, {
message: "Authentication required"
});
}
if (checks.roles && auth.user) {
const roles = auth.user.roles;
const hasRole = checks.roles.some((role) => {
return roles.includes(role) || roles.includes(`${AUTH_PROJECT_ID}:${role}`);
});
if (!hasRole) {
const resError = new Response("Forbidden", {
status: 403,
headers: {
"Missing-Roles": checks.roles.join(","),
"Obtained-Roles": roles.join(",")
}
});
throw new HTTPException2(resError.status, {
res: resError
});
}
}
return next();
};
return middleware;
};
function requireAuth(checks) {
const checkAuth = async (c) => {
const ctx = await c;
try {
await authMiddleware(checks)(ctx, async () => {
});
} catch (e) {
if (e instanceof HTTPException2) {
if (e.status === 401) {
throw new ServiceError(e.message, {
statusCode: 401,
code: "AUTH_REQUIRED"
});
} else if (e.status === 403) {
const res = e.getResponse();
throw new ServiceError(e.message, {
statusCode: res.status,
code: "AUTHORIZATION_REQUIRED",
details: {
missingRoles: res.headers.get("Missing-Roles")?.split(","),
obtainedRoles: res.headers.get("Obtained-Roles")?.split(",")
}
});
} else {
throw e;
}
}
throw e;
}
};
return createDecorator(async () => {
const ctx = getContext();
await checkAuth(ctx);
});
}
// src/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 } = await import("./setup-XP3KJV3R.js");
return setup(api);
},
build: async (api) => {
const { build } = await import("./build-TKNMTVMY.js");
return build(api);
}
};
}
// src/gateway.ts
import { delegateToSchema } from "@graphql-tools/delegate";
import { buildHTTPExecutor } from "@graphql-tools/executor-http";
import { schemaFromExecutor, wrapSchema } from "@graphql-tools/wrap";
import {
getNamedType as getNamedType2,
isEnumType,
Kind as Kind4,
OperationTypeNode,
TypeInfo,
visit,
visitWithTypeInfo
} from "graphql";
var schemaCache = /* @__PURE__ */ new Map();
function astFromJSValue(value) {
if (value === null || value === void 0) {
return { kind: Kind4.NULL };
}
if (typeof value === "string") {
return { kind: Kind4.STRING, value };
}
if (typeof value === "number") {
return Number.isInteger(value) ? { kind: Kind4.INT, value: String(value) } : { kind: Kind4.FLOAT, value: String(value) };
}
if (typeof value === "boolean") {
return { kind: Kind4.BOOLEAN, value };
}
if (Array.isArray(value)) {
return {
kind: Kind4.LIST,
values: value.map(astFromJSValue)
};
}
if (typeof value === "object") {
return {
kind: Kind4.OBJECT,
fields: Object.entries(value).filter(([_, val]) => val !== void 0).map(([key, val]) => ({
kind: Kind4.OBJECT_FIELD,
name: { kind: Kind4.NAME, value: key },
value: astFromJSValue(val)
}))
};
}
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;
}
var 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 };
}
};
var InlineArgsTransform = class {
constructor(wrapperArgs) {
this.wrapperArgs = wrapperArgs;
}
transformRequest(originalRequest, delegationContext) {
const rootInlineArguments = this.wrapperArgs && Object.keys(this.wrapperArgs).length > 0 ? Object.entries(this.wrapperArgs).filter(([_, val]) => val !== void 0).map(
([key, value]) => ({
kind: Kind4.ARGUMENT,
name: { kind: Kind4.NAME, value: key },
value: astFromJSValue(value)
})
) : [];
const targetFieldName = delegationContext.fieldName;
let targetFieldFound = false;
const variables = originalRequest.variables || {};
let inlineDocument = visit(originalRequest.document, {
// A. Inject our wrapper args into the ROOT field
Field(node) {
if (!targetFieldFound && node.name.value === targetFieldName) {
targetFieldFound = true;
const existingArgs = node.arguments || [];
const mergedArgs = [...existingArgs];
for (const newArg of rootInlineArguments) {
const existingIdx = mergedArgs.findIndex(
(a) => a.name.value === newArg.name.value
);
if (existingIdx > -1) mergedArgs[existingIdx] = newArg;
else mergedArgs.push(newArg);
}
return { ...node, arguments: mergedArgs };
}
},
// B. PRUNE missing arguments entirely
Argument(node) {
if (node.value.kind === Kind4.VARIABLE) {
const varName = node.value.name.value;
if (!(varName in variables) || variables[varName] === void 0) {
return null;
}
}
},
// C. PRUNE missing input object fields entirely (e.g. filters: { status: $missing })
ObjectField(node) {
if (node.value.kind === Kind4.VARIABLE) {
const varName = node.value.name.value;
if (!(varName in variables) || variables[varName] === void 0) {
return null;
}
}
},
// D. Inline all surviving variables (and safely fallback to NullNode just in case)
Variable(node) {
const varName = node.name.value;
return astFromJSValue(variables[varName]);
},
// E. Wipe variable definitions
OperationDefinition(node) {
return { ...node, variableDefinitions: [] };
}
});
const typeInfo = new TypeInfo(delegationContext.targetSchema);
inlineDocument = visit(
inlineDocument,
visitWithTypeInfo(typeInfo, {
StringValue(node) {
const inputType = typeInfo.getInputType();
if (inputType) {
const namedType = getNamedType2(inputType);
if (isEnumType(namedType)) {
return { kind: Kind4.ENUM, value: node.value };
}
}
}
})
);
return {
...originalRequest,
document: inlineDocument,
variables: {}
// Wipe the payload, we don't need it anymore!
};
}
};
var 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;
}
};
var PylonGateway = class {
constructor(config) {
this.config = config;
this.apiContext = {
delegate: this.delegate.bind(this)
};
}
apiContext;
async delegate(key, ...opts) {
const { info } = getResolveInfo();
const ctx = getContext();
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 InlineArgsTransform(args),
// Injects arguments into the AST
new PylonPatchTransform(this.config.patches, this.apiContext)
]
});
return result;
}
};
function createGateway() {
return {
configure: (config) => {
return new PylonGateway(config);
}
};
}
export {
asyncContext,
getContext,
setContext,
app,
getResolveInfo,
ServiceError,
executeConfig,
handler,
createDecorator,
getEnv,
useAuth,
authMiddleware,
requireAuth,
usePages,
createGateway,
createPubSub
};
//# sourceMappingURL=chunk-2W3G375V.js.map