UNPKG

@pulzar/core

Version:

Next-generation Node.js framework for ultra-fast web applications with zero-reflection DI, GraphQL, WebSockets, events, and edge runtime support

264 lines (257 loc) 8.76 kB
import { logger } from "../utils/logger"; export class MercuriusAdapter { app; options; resolvers = new Map(); schema = ""; initialized = false; constructor(options = {}) { this.options = { schema: "", subscriptions: true, playground: process.env.NODE_ENV !== "production", path: "/graphql", jit: 1, cache: 4096, ...options, }; } async initialize(app) { if (this.initialized) { throw new Error("MercuriusAdapter already initialized"); } this.app = app; try { // Try to load Mercurius dynamically const mercuriusModule = await this.dynamicImportMercurius(); if (mercuriusModule) { const mercurius = mercuriusModule.default || mercuriusModule; // Register Mercurius plugin with Fastify await app.register(mercurius, { schema: this.schema || this.getDefaultSchema(), resolvers: this.buildResolvers(), graphiql: this.options.playground, path: this.options.path, subscription: this.options.subscriptions, jit: this.options.jit, cache: this.options.cache, context: async (request, reply) => { return { request, reply, user: request.user, }; }, }); logger.info("Mercurius GraphQL adapter initialized", { path: this.options.path, subscriptions: this.options.subscriptions, playground: this.options.playground, schema: !!this.schema, resolvers: this.resolvers.size, }); } else { // Fallback to mock implementation logger.warn("Mercurius package not installed, using mock implementation"); this.createMockMercurius(app); } this.initialized = true; } catch (error) { logger.error("Failed to initialize Mercurius GraphQL server", { error }); throw error; } } setSchema(schema) { this.schema = schema; logger.debug("GraphQL schema set", { length: schema.length }); } addResolver(typeName, resolvers) { this.resolvers.set(typeName, resolvers); logger.debug("GraphQL resolver added", { typeName }); } async executeQuery(query, variables, context) { if (!this.initialized) { throw new Error("MercuriusAdapter not initialized"); } try { if (this.app && this.app.graphql) { // Use Mercurius to execute query const result = await this.app.graphql(query, context, variables); logger.debug("GraphQL query executed", { query: query.substring(0, 100) + (query.length > 100 ? "..." : ""), variables: variables ? Object.keys(variables) : undefined, hasErrors: !!result.errors, }); return result; } else { // Mock implementation logger.debug("GraphQL query executed (mock)", { query: query.substring(0, 50) + "...", variables, }); return { data: { mock: "This is a mock GraphQL response", query: query.substring(0, 100), variables, }, }; } } catch (error) { logger.error("GraphQL query execution failed", { query, variables, error, }); return { data: null, errors: [ { message: error instanceof Error ? error.message : "Unknown GraphQL error", extensions: { code: "EXECUTION_ERROR", }, }, ], }; } } async dynamicImportMercurius() { try { return await new Function('return import("mercurius")')(); } catch { return null; } } buildResolvers() { const resolvers = {}; for (const [typeName, typeResolvers] of this.resolvers.entries()) { resolvers[typeName] = typeResolvers; } // Add default resolvers if none provided if (Object.keys(resolvers).length === 0) { resolvers.Query = { hello: () => "Hello from Pulzar GraphQL!", version: () => "1.0.0", }; } return resolvers; } getDefaultSchema() { if (this.schema) { return this.schema; } return ` type Query { hello: String version: String } type Mutation { placeholder: String } `; } createMockMercurius(app) { // Create mock GraphQL endpoint app.post(this.options.path, async (request, reply) => { const { query, variables } = request.body || {}; const result = await this.executeQuery(query, variables, { request, reply, }); return result; }); if (this.options.playground) { // Mock GraphiQL playground app.get(this.options.path, async (request, reply) => { reply.type("text/html"); return this.getPlaygroundHTML(); }); } logger.info("Mock GraphQL endpoint created", { path: this.options.path }); } getPlaygroundHTML() { return ` <!DOCTYPE html> <html> <head> <title>GraphQL Playground (Mock)</title> <style> body { font-family: Arial, sans-serif; padding: 20px; } .container { max-width: 800px; margin: 0 auto; } .warning { background: #fff3cd; padding: 15px; border-radius: 5px; margin-bottom: 20px; } textarea { width: 100%; height: 200px; font-family: monospace; } button { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; } .result { background: #f8f9fa; padding: 15px; border-radius: 5px; margin-top: 20px; white-space: pre-wrap; font-family: monospace; } </style> </head> <body> <div class="container"> <h1>GraphQL Playground (Mock)</h1> <div class="warning"> <strong>Warning:</strong> This is a mock GraphQL playground. Install 'mercurius' package for full functionality. </div> <h3>Query:</h3> <textarea id="query" placeholder="Enter your GraphQL query here..."> query { hello version } </textarea> <br><br> <button onclick="executeQuery()">Execute Query</button> <h3>Result:</h3> <div id="result" class="result">Click "Execute Query" to see results</div> </div> <script> async function executeQuery() { const query = document.getElementById('query').value; const resultDiv = document.getElementById('result'); try { const response = await fetch('${this.options.path}', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ query }), }); const result = await response.json(); resultDiv.textContent = JSON.stringify(result, null, 2); } catch (error) { resultDiv.textContent = 'Error: ' + error.message; } } </script> </body> </html> `; } getStats() { return { initialized: this.initialized, schema: !!this.schema, resolvers: this.resolvers.size, path: this.options.path, }; } async shutdown() { this.resolvers.clear(); this.schema = ""; this.initialized = false; logger.info("Mercurius GraphQL adapter shutdown"); } } export function Resolver(typeName) { return function (target) { const resolverName = typeName || target.name.replace(/Resolver$/, ""); Reflect.defineMetadata("graphql:resolver", { typeName: resolverName }, target); return target; }; } export default MercuriusAdapter; //# sourceMappingURL=mercurius-adapter.js.map