UNPKG

query-2jz

Version:

Query-2jz: A GraphQL alternative with faster performance and simpler use

340 lines (283 loc) 9.53 kB
import { Query2jzConfig, Query2jzModel, Query2jzQuery, Query2jzMutation, Query2jzResponse } from './types'; import { Query2jzResolver } from './core/resolver'; import { Query2jzCache } from './core/cache'; import { Query2jzRealtime } from './core/realtime'; import { Query2jzOptimisticEngine } from './core/optimistic'; import { Query2jzTypeGenerator } from './core/typegen'; import { Query2jzEdgeRuntime, Query2jzEdgeHandler } from './core/edge'; import { FastifyInstance, FastifyPluginOptions } from 'fastify'; import fastify from 'fastify'; /** * Main Query-2jz Engine - GraphQL alternative with faster performance and simpler use */ export class Query2jz { private config: Query2jzConfig; private models: Query2jzModel[]; private resolver?: Query2jzResolver; private cache?: Query2jzCache; private realtime?: Query2jzRealtime; private optimistic?: Query2jzOptimisticEngine; private typeGenerator?: Query2jzTypeGenerator; private edgeRuntime?: Query2jzEdgeRuntime; private server?: FastifyInstance; private isInitialized = false; constructor(config: Query2jzConfig, models: Query2jzModel[] = []) { this.config = config; this.models = models; } /** * Initialize Query-2jz engine */ async initialize(): Promise<void> { if (this.isInitialized) return; console.log('Initializing Query-2jz engine...'); // Initialize core components this.resolver = new Query2jzResolver(this.config, this.models); this.cache = new Query2jzCache(this.config); this.optimistic = new Query2jzOptimisticEngine(); this.typeGenerator = new Query2jzTypeGenerator(this.models); // Initialize real-time if enabled if (this.config.realtime?.enabled) { this.realtime = new Query2jzRealtime(this.config); } // Initialize edge runtime if enabled if (this.config.edge?.enabled) { this.edgeRuntime = new Query2jzEdgeRuntime(this.config); await this.edgeRuntime.initialize(); } this.isInitialized = true; console.log('Query-2jz engine initialized successfully'); } /** * Start the Query-2jz server */ async start(port: number = 3000): Promise<void> { await this.ensureInitialized(); this.server = fastify({ logger: true, trustProxy: true }); // Register Query-2jz routes await this.server.register(this.createQuery2jzRoutes.bind(this)); // Initialize real-time if enabled if (this.realtime) { await this.realtime.initialize(this.server.server); } // Start server await this.server.listen({ port, host: '0.0.0.0' }); console.log(`Query-2jz server running on port ${port}`); } /** * Stop the Query-2jz server */ async stop(): Promise<void> { if (this.server) { await this.server.close(); } if (this.realtime) { await this.realtime.cleanup(); } if (this.edgeRuntime) { await this.edgeRuntime.cleanup(); } console.log('Query-2jz server stopped'); } /** * Execute a query */ async query(query: Query2jzQuery, modelName: string): Promise<Query2jzResponse> { await this.ensureInitialized(); if (this.edgeRuntime) { return await this.edgeRuntime.executeQuery(query, modelName); } return await this.resolver!.executeQuery(query, modelName); } /** * Execute a mutation */ async mutate(mutation: Query2jzMutation, modelName: string): Promise<Query2jzResponse> { await this.ensureInitialized(); if (this.edgeRuntime) { return await this.edgeRuntime.executeMutation(mutation, modelName); } return await this.resolver!.executeMutation(mutation, modelName); } /** * Subscribe to live query updates */ subscribe( query: Query2jzQuery, modelName: string, callback: (data: any) => void ): string | null { if (!this.realtime) { console.warn('Real-time is not enabled'); return null; } return this.realtime.subscribe(query, modelName, callback); } /** * Unsubscribe from live query updates */ unsubscribe(subscriptionId: string): void { if (this.realtime) { this.realtime.unsubscribe(subscriptionId); } } /** * Generate TypeScript types */ generateTypes(outputDir?: string): void { if (!this.typeGenerator) { throw new Error('Query-2jz not initialized'); } if (outputDir) { this.typeGenerator = new Query2jzTypeGenerator(this.models, outputDir); } this.typeGenerator.generateTypes(); console.log(`Types generated in ${outputDir || './generated'}`); } /** * Add a model */ addModel(model: Query2jzModel): void { this.models.push(model); if (this.resolver) { this.resolver.addModel(model); } if (this.typeGenerator) { this.typeGenerator = new Query2jzTypeGenerator(this.models); } } /** * Remove a model */ removeModel(modelName: string): void { this.models = this.models.filter(m => m.name !== modelName); if (this.resolver) { this.resolver.removeModel(modelName); } if (this.typeGenerator) { this.typeGenerator = new Query2jzTypeGenerator(this.models); } } /** * Get all models */ getModels(): Query2jzModel[] { return [...this.models]; } /** * Get model by name */ getModel(modelName: string): Query2jzModel | undefined { return this.models.find(m => m.name === modelName); } /** * Get Query-2jz statistics */ getStats(): any { return { initialized: this.isInitialized, models: this.models.length, cache: this.cache?.getStats(), realtime: this.realtime?.getStats(), optimistic: this.optimistic?.getStats(), edge: this.edgeRuntime?.getStats() }; } /** * Create Query-2jz routes for Fastify */ private async createQuery2jzRoutes( fastify: FastifyInstance, options: FastifyPluginOptions ): Promise<void> { // Health check fastify.get('/health', async (request, reply) => { return { status: 'healthy', timestamp: new Date().toISOString() }; }); // Query endpoint fastify.get('/api/query-2jz/:model', async (request, reply) => { const { model } = request.params as { model: string }; const query = request.query as any; try { const result = await this.query(query, model); // Set cache headers if (result.meta?.etag) { reply.header('ETag', result.meta.etag); } if (result.meta?.lastModified) { reply.header('Last-Modified', result.meta.lastModified); } reply.header('Cache-Control', 'public, max-age=300'); return result; } catch (error) { reply.code(500); return { error: 'Query execution failed', message: error.message }; } }); // Mutation endpoint fastify.post('/api/query-2jz/:model', async (request, reply) => { const { model } = request.params as { model: string }; const mutation = request.body as Query2jzMutation; try { const result = await this.mutate(mutation, model); reply.header('Cache-Control', 'no-cache'); return result; } catch (error) { reply.code(500); return { error: 'Mutation execution failed', message: error.message }; } }); // Real-time subscription endpoint if (this.realtime) { fastify.get('/api/query-2jz/:model/subscribe', async (request, reply) => { const { model } = request.params as { model: string }; const query = request.query as any; // This would handle SSE connections reply.type('text/event-stream'); reply.header('Cache-Control', 'no-cache'); reply.header('Connection', 'keep-alive'); const subscriptionId = this.subscribe(query, model, (data) => { reply.raw.write(`data: ${JSON.stringify(data)}\n\n`); }); request.raw.on('close', () => { if (subscriptionId) { this.unsubscribe(subscriptionId); } }); return reply; }); } // Type generation endpoint fastify.post('/api/query-2jz/generate-types', async (request, reply) => { try { this.generateTypes(); return { message: 'Types generated successfully' }; } catch (error) { reply.code(500); return { error: 'Type generation failed', message: error.message }; } }); // Statistics endpoint fastify.get('/api/query-2jz/stats', async (request, reply) => { return this.getStats(); }); } private async ensureInitialized(): Promise<void> { if (!this.isInitialized) { await this.initialize(); } } } // Export types and classes export * from './types'; export { Query2jzResolver } from './core/resolver'; export { Query2jzCache } from './core/cache'; export { Query2jzRealtime } from './core/realtime'; export { Query2jzOptimisticEngine } from './core/optimistic'; export { Query2jzTypeGenerator } from './core/typegen'; export { Query2jzEdgeRuntime, Query2jzEdgeHandler } from './core/edge'; // Default export export default Query2jz;