@egdesk/next-api-plugin 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # @egdesk/next-api-plugin
2
+
3
+ Next.js plugin for EGDesk database proxy integration. Provides middleware-based CORS-free database access for Next.js applications.
4
+
5
+ ## Features
6
+
7
+ - 🔒 CORS-free database access via Next.js middleware
8
+ - 🌐 Works in both local and tunneled environments
9
+ - 📝 Type-safe table definitions and helper functions
10
+ - 🚀 Auto-discovery of database tables
11
+ - 🔧 Zero configuration after setup
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @egdesk/next-api-plugin
17
+ # or
18
+ yarn add @egdesk/next-api-plugin
19
+ # or
20
+ pnpm add @egdesk/next-api-plugin
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ 1. Run the setup command in your Next.js project:
26
+
27
+ ```bash
28
+ npx egdesk-next-setup
29
+ ```
30
+
31
+ This will generate:
32
+ - `middleware.ts` - Database proxy middleware
33
+ - `egdesk.config.ts` - Type-safe table definitions
34
+ - `egdesk-helpers.ts` - Helper functions for database access
35
+ - `.env.local` - Environment variables
36
+
37
+ 2. Add `.env.local` to your `.gitignore` (if not already there)
38
+
39
+ 3. Restart your Next.js dev server
40
+
41
+ 4. Use the helpers in your components:
42
+
43
+ ```typescript
44
+ import { queryTable } from './egdesk-helpers';
45
+ import { TABLES } from './egdesk.config';
46
+
47
+ export default async function MyPage() {
48
+ const data = await queryTable(TABLES.table1.name, { limit: 10 });
49
+
50
+ return (
51
+ <div>
52
+ <pre>{JSON.stringify(data, null, 2)}</pre>
53
+ </div>
54
+ );
55
+ }
56
+ ```
57
+
58
+ ## Configuration
59
+
60
+ ### Environment Variables
61
+
62
+ The plugin uses Next.js environment variables:
63
+
64
+ ```env
65
+ NEXT_PUBLIC_EGDESK_API_URL=http://localhost:8080
66
+ NEXT_PUBLIC_EGDESK_API_KEY=your-api-key-here
67
+ ```
68
+
69
+ ### Custom Setup
70
+
71
+ You can programmatically run the setup:
72
+
73
+ ```typescript
74
+ import { setupNextApiPlugin } from '@egdesk/next-api-plugin';
75
+
76
+ await setupNextApiPlugin('/path/to/project', {
77
+ egdeskUrl: 'http://localhost:8080',
78
+ apiKey: 'optional-api-key'
79
+ });
80
+ ```
81
+
82
+ ## How It Works
83
+
84
+ The plugin creates a Next.js middleware that intercepts requests to `__user_data_proxy` and forwards them to your EGDesk MCP server. This allows your Next.js app to make database queries without CORS issues, even in tunneled environments.
85
+
86
+ **Request Flow:**
87
+ 1. Your component calls `queryTable()` or other helpers
88
+ 2. Helper makes a fetch to `__user_data_proxy`
89
+ 3. Next.js middleware intercepts the request
90
+ 4. Middleware forwards to `localhost:8080/user-data/tools/call`
91
+ 5. Response is returned to your component
92
+
93
+ ## API Reference
94
+
95
+ ### Helper Functions
96
+
97
+ ```typescript
98
+ // Query table data
99
+ queryTable(tableName: string, options?: {
100
+ filters?: Record<string, string>;
101
+ limit?: number;
102
+ offset?: number;
103
+ orderBy?: string;
104
+ orderDirection?: 'ASC' | 'DESC';
105
+ })
106
+
107
+ // Search table
108
+ searchTable(tableName: string, searchQuery: string, limit?: number)
109
+
110
+ // Aggregate data
111
+ aggregateTable(tableName: string, column: string, aggregateFunction: 'SUM' | 'AVG' | 'MIN' | 'MAX' | 'COUNT', options?: {
112
+ filters?: Record<string, string>;
113
+ groupBy?: string;
114
+ })
115
+
116
+ // Execute raw SQL
117
+ executeSQL(query: string)
118
+
119
+ // List all tables
120
+ listTables()
121
+
122
+ // Get table schema
123
+ getTableSchema(tableName: string)
124
+ ```
125
+
126
+ ### Configuration Types
127
+
128
+ ```typescript
129
+ interface TableDefinition {
130
+ name: string;
131
+ displayName: string;
132
+ description?: string;
133
+ rowCount: number;
134
+ columnCount: number;
135
+ columns: string[];
136
+ }
137
+
138
+ const TABLES = {
139
+ table1: TableDefinition,
140
+ table2: TableDefinition,
141
+ // ...
142
+ }
143
+
144
+ const TABLE_NAMES = {
145
+ table1: 'actual_table_name',
146
+ table2: 'another_table_name',
147
+ // ...
148
+ }
149
+ ```
150
+
151
+ ## Troubleshooting
152
+
153
+ ### Middleware not working
154
+
155
+ Make sure:
156
+ - `middleware.ts` is in your project root (not in `src/` or `app/`)
157
+ - Your Next.js version is 13.0.0 or higher
158
+ - You've restarted your dev server after setup
159
+
160
+ ### CORS errors
161
+
162
+ If you're still seeing CORS errors:
163
+ - Check that middleware.ts was generated correctly
164
+ - Verify environment variables are set in `.env.local`
165
+ - Make sure you're using the relative URL `__user_data_proxy` (no leading slash)
166
+
167
+ ### Table discovery fails
168
+
169
+ Ensure:
170
+ - EGDesk MCP server is running on `localhost:8080`
171
+ - You have tables imported in EGDesk
172
+ - API key is correct (if required)
173
+
174
+ ## License
175
+
176
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point for @egdesk/next-api-plugin
4
+ *
5
+ * Usage:
6
+ * npx egdesk-next-setup
7
+ * npx egdesk-next-setup --url http://localhost:8080 --api-key YOUR_KEY
8
+ */
9
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * CLI entry point for @egdesk/next-api-plugin
5
+ *
6
+ * Usage:
7
+ * npx egdesk-next-setup
8
+ * npx egdesk-next-setup --url http://localhost:8080 --api-key YOUR_KEY
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ const index_1 = require("./index");
12
+ async function main() {
13
+ const args = process.argv.slice(2);
14
+ // Parse command line arguments
15
+ let egdeskUrl = 'http://localhost:8080';
16
+ let apiKey;
17
+ let useProxy = true; // Use proxy.ts by default (Next.js 16+)
18
+ for (let i = 0; i < args.length; i++) {
19
+ if (args[i] === '--url' && i + 1 < args.length) {
20
+ egdeskUrl = args[i + 1];
21
+ i++;
22
+ }
23
+ else if (args[i] === '--api-key' && i + 1 < args.length) {
24
+ apiKey = args[i + 1];
25
+ i++;
26
+ }
27
+ else if (args[i] === '--legacy-middleware') {
28
+ useProxy = false;
29
+ }
30
+ else if (args[i] === '--help' || args[i] === '-h') {
31
+ console.log('Usage: egdesk-next-setup [options]');
32
+ console.log('');
33
+ console.log('Options:');
34
+ console.log(' --url <url> EGDesk server URL (default: http://localhost:8080)');
35
+ console.log(' --api-key <key> API key for authentication');
36
+ console.log(' --legacy-middleware Use middleware.ts instead of proxy.ts (for Next.js <16)');
37
+ console.log(' --help, -h Show this help message');
38
+ console.log('');
39
+ console.log('Example:');
40
+ console.log(' egdesk-next-setup --url http://localhost:8080 --api-key mykey');
41
+ console.log(' egdesk-next-setup --legacy-middleware # For Next.js <16');
42
+ process.exit(0);
43
+ }
44
+ }
45
+ // Get current working directory (project root)
46
+ const projectPath = process.cwd();
47
+ console.log('🚀 EGDesk Next.js Setup');
48
+ console.log(`📂 Project: ${projectPath}`);
49
+ console.log(`🔗 EGDesk URL: ${egdeskUrl}`);
50
+ console.log('');
51
+ try {
52
+ await (0, index_1.setupNextApiPlugin)(projectPath, { egdeskUrl, apiKey, useProxy });
53
+ process.exit(0);
54
+ }
55
+ catch (error) {
56
+ console.error('Setup failed:', error);
57
+ process.exit(1);
58
+ }
59
+ }
60
+ main();
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generate helper functions for EGDesk database access in Next.js
3
+ *
4
+ * Creates egdesk-helpers.ts with type-safe helper functions.
5
+ */
6
+ /**
7
+ * Generate egdesk-helpers.ts file
8
+ */
9
+ export declare function generateHelpers(projectPath: string): void;
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ /**
3
+ * Generate helper functions for EGDesk database access in Next.js
4
+ *
5
+ * Creates egdesk-helpers.ts with type-safe helper functions.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.generateHelpers = generateHelpers;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ /**
45
+ * Generate egdesk-helpers.ts file
46
+ */
47
+ function generateHelpers(projectPath) {
48
+ const helperPath = path.join(projectPath, 'egdesk-helpers.ts');
49
+ const helperContent = `/**
50
+ * EGDesk User Data Helper Functions for Next.js
51
+ *
52
+ * Type-safe helpers for accessing EGDesk user data.
53
+ * Works in both client and server components.
54
+ *
55
+ * Generated by @egdesk/next-api-plugin
56
+ */
57
+
58
+ /**
59
+ * Call EGDesk user-data MCP tool
60
+ *
61
+ * Uses a proxy endpoint to work in both local and tunneled environments.
62
+ * The Next.js middleware intercepts these requests and forwards them to localhost:8080.
63
+ */
64
+ export async function callUserDataTool(
65
+ toolName: string,
66
+ args: Record<string, any> = {}
67
+ ): Promise<any> {
68
+ const headers: Record<string, string> = {
69
+ 'Content-Type': 'application/json'
70
+ };
71
+
72
+ // Use proxy endpoint - works in both local and tunneled environments
73
+ // Absolute URL with leading slash to ensure correct resolution from any route
74
+ const response = await fetch('/__user_data_proxy', {
75
+ method: 'POST',
76
+ headers,
77
+ body: JSON.stringify({ tool: toolName, arguments: args })
78
+ });
79
+
80
+ if (!response.ok) {
81
+ throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
82
+ }
83
+
84
+ const result = await response.json();
85
+
86
+ if (!result.success) {
87
+ throw new Error(result.error || 'Tool call failed');
88
+ }
89
+
90
+ // Parse MCP response format
91
+ const content = result.result?.content?.[0]?.text;
92
+ return content ? JSON.parse(content) : null;
93
+ }
94
+
95
+ /**
96
+ * Query table data
97
+ */
98
+ export async function queryTable(
99
+ tableName: string,
100
+ options: {
101
+ filters?: Record<string, string>;
102
+ limit?: number;
103
+ offset?: number;
104
+ orderBy?: string;
105
+ orderDirection?: 'ASC' | 'DESC';
106
+ } = {}
107
+ ) {
108
+ return callUserDataTool('user_data_query', {
109
+ tableName,
110
+ ...options
111
+ });
112
+ }
113
+
114
+ /**
115
+ * Search table
116
+ */
117
+ export async function searchTable(
118
+ tableName: string,
119
+ searchQuery: string,
120
+ limit: number = 50
121
+ ) {
122
+ return callUserDataTool('user_data_search', {
123
+ tableName,
124
+ searchQuery,
125
+ limit
126
+ });
127
+ }
128
+
129
+ /**
130
+ * Aggregate data
131
+ */
132
+ export async function aggregateTable(
133
+ tableName: string,
134
+ column: string,
135
+ aggregateFunction: 'SUM' | 'AVG' | 'MIN' | 'MAX' | 'COUNT',
136
+ options: {
137
+ filters?: Record<string, string>;
138
+ groupBy?: string;
139
+ } = {}
140
+ ) {
141
+ return callUserDataTool('user_data_aggregate', {
142
+ tableName,
143
+ column,
144
+ function: aggregateFunction,
145
+ ...options
146
+ });
147
+ }
148
+
149
+ /**
150
+ * Execute raw SQL query
151
+ */
152
+ export async function executeSQL(query: string) {
153
+ return callUserDataTool('user_data_sql_query', { query });
154
+ }
155
+
156
+ /**
157
+ * List all available tables
158
+ */
159
+ export async function listTables() {
160
+ return callUserDataTool('user_data_list_tables', {});
161
+ }
162
+
163
+ /**
164
+ * Get table schema
165
+ */
166
+ export async function getTableSchema(tableName: string) {
167
+ return callUserDataTool('user_data_get_schema', { tableName });
168
+ }
169
+ `;
170
+ fs.writeFileSync(helperPath, helperContent, 'utf-8');
171
+ console.log(`✅ Generated ${helperPath}`);
172
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Generate Next.js middleware for EGDesk database proxy
3
+ *
4
+ * Creates middleware.ts that intercepts __user_data_proxy requests
5
+ * and forwards them to the EGDesk MCP server.
6
+ */
7
+ /**
8
+ * Generate middleware.ts file in the correct location (root or src/)
9
+ */
10
+ export declare function generateMiddleware(projectPath: string): void;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ /**
3
+ * Generate Next.js middleware for EGDesk database proxy
4
+ *
5
+ * Creates middleware.ts that intercepts __user_data_proxy requests
6
+ * and forwards them to the EGDesk MCP server.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.generateMiddleware = generateMiddleware;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ /**
46
+ * Detect if project uses src/ directory structure
47
+ */
48
+ function usesSrcDirectory(projectPath) {
49
+ const srcPath = path.join(projectPath, 'src');
50
+ const srcAppPath = path.join(projectPath, 'src', 'app');
51
+ return fs.existsSync(srcPath) && fs.existsSync(srcAppPath);
52
+ }
53
+ /**
54
+ * Generate middleware.ts file in the correct location (root or src/)
55
+ */
56
+ function generateMiddleware(projectPath) {
57
+ // Detect if project uses src/ directory
58
+ const hasSrc = usesSrcDirectory(projectPath);
59
+ const targetDir = hasSrc ? path.join(projectPath, 'src') : projectPath;
60
+ const middlewarePath = path.join(targetDir, 'middleware.ts');
61
+ console.log(`📁 Detected ${hasSrc ? 'src/' : 'root'} directory structure`);
62
+ console.log(`📝 Generating middleware at: ${middlewarePath}`);
63
+ // Check if middleware.ts already exists
64
+ if (fs.existsSync(middlewarePath)) {
65
+ console.log('⚠️ middleware.ts already exists - backing up to middleware.backup.ts');
66
+ const backupPath = path.join(targetDir, 'middleware.backup.ts');
67
+ fs.copyFileSync(middlewarePath, backupPath);
68
+ }
69
+ const middlewareContent = `import { NextResponse } from 'next/server';
70
+ import type { NextRequest } from 'next/server';
71
+
72
+ /**
73
+ * EGDesk Database Proxy Middleware
74
+ *
75
+ * Intercepts __user_data_proxy requests and forwards them to the EGDesk MCP server.
76
+ * This allows CORS-free database access in both local and tunneled environments.
77
+ *
78
+ * Generated by @egdesk/next-api-plugin
79
+ */
80
+ export async function middleware(request: NextRequest) {
81
+ const { pathname } = request.nextUrl;
82
+
83
+ // Intercept __user_data_proxy requests
84
+ if (pathname.includes('__user_data_proxy')) {
85
+ try {
86
+ const body = await request.text();
87
+
88
+ // Read API key and URL from environment
89
+ const apiKey = process.env.NEXT_PUBLIC_EGDESK_API_KEY;
90
+ const apiUrl = process.env.NEXT_PUBLIC_EGDESK_API_URL || 'http://localhost:8080';
91
+
92
+ const headers: HeadersInit = {
93
+ 'Content-Type': 'application/json',
94
+ };
95
+
96
+ if (apiKey) {
97
+ headers['X-Api-Key'] = apiKey;
98
+ }
99
+
100
+ // Forward to EGDesk MCP server
101
+ const response = await fetch(\`\${apiUrl}/user-data/tools/call\`, {
102
+ method: 'POST',
103
+ headers,
104
+ body,
105
+ });
106
+
107
+ const result = await response.json();
108
+
109
+ return NextResponse.json(result, { status: response.status });
110
+ } catch (error: any) {
111
+ return NextResponse.json(
112
+ { error: 'Proxy error', message: error.message },
113
+ { status: 500 }
114
+ );
115
+ }
116
+ }
117
+
118
+ // Continue to next middleware or route
119
+ return NextResponse.next();
120
+ }
121
+
122
+ export const config = {
123
+ matcher: [
124
+ /*
125
+ * Match all request paths except for the ones starting with:
126
+ * - api (API routes)
127
+ * - _next/static (static files)
128
+ * - _next/image (image optimization files)
129
+ * - favicon.ico (favicon file)
130
+ */
131
+ '/((?!api|_next/static|_next/image|favicon.ico).*)',
132
+ ],
133
+ };
134
+ `;
135
+ fs.writeFileSync(middlewarePath, middlewareContent, 'utf-8');
136
+ console.log(`✅ Generated ${middlewarePath}`);
137
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Generate Next.js 16+ proxy for EGDesk database proxy
3
+ *
4
+ * Creates proxy.ts that intercepts __user_data_proxy requests
5
+ * and forwards them to the EGDesk MCP server.
6
+ *
7
+ * This is the Next.js 16+ recommended approach (replaces middleware.ts)
8
+ */
9
+ /**
10
+ * Generate proxy.ts file in the correct location (root or src/)
11
+ */
12
+ export declare function generateProxy(projectPath: string): void;