elit 3.1.6 → 3.1.8

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/src/server.ts CHANGED
@@ -16,23 +16,30 @@ import { lookup } from './mime-types';
16
16
  import { isBun, isDeno } from './runtime';
17
17
  import type { DevServerOptions, DevServer, HMRMessage, Child, VNode, ProxyConfig } from './types';
18
18
  import { dom } from './dom';
19
+ import { Database, DatabaseConfig } from './database';
19
20
 
20
21
  // ===== Router =====
21
22
 
22
23
  export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD' | 'ALL';
23
24
 
25
+ export interface ElitRequest extends IncomingMessage {
26
+ body?: any;
27
+ }
28
+
29
+ export interface ElitResponse extends ServerResponse {
30
+ json(data: any, statusCode?: number): this;
31
+ send(data: any): this;
32
+ status(code: number): this;
33
+ }
34
+
24
35
  export interface ServerRouteContext {
25
- req: IncomingMessage;
26
- res: ServerResponse;
36
+ req: ElitRequest;
37
+ res: ElitResponse;
27
38
  params: Record<string, string>;
28
39
  query: Record<string, string>;
29
40
  body: any;
30
41
  headers: Record<string, string | string[] | undefined>;
31
42
  user?: any;
32
- // Express-compatible response helpers
33
- send?(data: any): ServerResponse;
34
- json?(data: any): ServerResponse;
35
- status?(code: number): ServerResponse;
36
43
  }
37
44
 
38
45
  export type ServerRouteHandler = (ctx: ServerRouteContext, next?: () => Promise<void>) => void | Promise<void>;
@@ -46,6 +53,25 @@ interface ServerRoute {
46
53
  middlewares: Middleware[];
47
54
  }
48
55
 
56
+ class ServerDatabase {
57
+ private _db: Database | null = null;
58
+
59
+ constructor() {
60
+
61
+ }
62
+
63
+ async initialize(config: DatabaseConfig) {
64
+ this._db = new Database(config);
65
+ }
66
+
67
+ database() {
68
+ return this._db;
69
+ }
70
+ }
71
+
72
+ export const serverDatabase = new ServerDatabase();
73
+
74
+ export const database = serverDatabase.database;
49
75
  export class ServerRouter {
50
76
  private routes: ServerRoute[] = [];
51
77
  private middlewares: Middleware[] = [];
@@ -66,19 +92,19 @@ export class ServerRouter {
66
92
  }
67
93
 
68
94
  // Express-like .all() method - matches all HTTP methods
69
- all = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: IncomingMessage, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('ALL', path, handlers as any);
95
+ all = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: ElitRequest, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('ALL', path, handlers as any);
70
96
 
71
97
  // Support per-route middleware: accept middleware(s) before the final handler
72
- get = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: IncomingMessage, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('GET', path, handlers as any);
73
- post = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: IncomingMessage, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('POST', path, handlers as any);
74
- put = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: IncomingMessage, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('PUT', path, handlers as any);
75
- delete = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: IncomingMessage, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('DELETE', path, handlers as any);
76
- patch = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: IncomingMessage, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('PATCH', path, handlers as any);
77
- options = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: IncomingMessage, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('OPTIONS', path, handlers as any);
78
- head = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: IncomingMessage, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('HEAD', path, handlers as any);
98
+ get = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: ElitRequest, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('GET', path, handlers as any);
99
+ post = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: ElitRequest, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('POST', path, handlers as any);
100
+ put = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: ElitRequest, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('PUT', path, handlers as any);
101
+ delete = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: ElitRequest, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('DELETE', path, handlers as any);
102
+ patch = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: ElitRequest, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('PATCH', path, handlers as any);
103
+ options = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: ElitRequest, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('OPTIONS', path, handlers as any);
104
+ head = (path: string, ...handlers: Array<Middleware | ServerRouteHandler | ((req: ElitRequest, res: ServerResponse, next?: () => void) => any)>): this => this.addRoute('HEAD', path, handlers as any);
79
105
 
80
106
  // Convert Express-like handler/middleware to internal Middleware
81
- private toMiddleware(fn: Middleware | ServerRouteHandler | ((req: IncomingMessage, res: ServerResponse, next?: () => void) => any)): Middleware {
107
+ private toMiddleware(fn: Middleware | ServerRouteHandler | ((req: ElitRequest, res: ServerResponse, next?: () => void) => any)): Middleware {
82
108
  // If it's already our Middleware, return as-is
83
109
  if ((fn as Middleware).length === 2 && (fn as any).name !== 'bound ') {
84
110
  // Cannot reliably detect, so always wrap to normalize behavior
@@ -116,7 +142,7 @@ export class ServerRouter {
116
142
  };
117
143
  }
118
144
 
119
- private addRoute(method: HttpMethod, path: string, handlers: Array<Middleware | ServerRouteHandler | ((req: IncomingMessage, res: ServerResponse, next?: () => void) => any)>): this {
145
+ private addRoute(method: HttpMethod, path: string, handlers: Array<Middleware | ServerRouteHandler | ((req: ElitRequest, res: ServerResponse, next?: () => void) => any)>): this {
120
146
  const { pattern, paramNames } = this.pathToRegex(path);
121
147
  // Last item is the actual route handler, preceding items are middlewares
122
148
  if (!handlers || handlers.length === 0) throw new Error('Route must include a handler');
@@ -173,10 +199,10 @@ export class ServerRouter {
173
199
  try {
174
200
  const text = await (req as any).text();
175
201
  if (!text) return {};
176
-
202
+
177
203
  const contentType = req.headers['content-type'];
178
204
  const ct = (Array.isArray(contentType) ? contentType[0] : (contentType || '')).toLowerCase();
179
-
205
+
180
206
  // Parse JSON (either by content-type or if it looks like JSON)
181
207
  if (ct.includes('application/json') || ct.includes('json') || text.trim().startsWith('{') || text.trim().startsWith('[')) {
182
208
  try {
@@ -185,12 +211,12 @@ export class ServerRouter {
185
211
  return text;
186
212
  }
187
213
  }
188
-
214
+
189
215
  // Parse URL-encoded
190
216
  if (ct.includes('application/x-www-form-urlencoded') || ct.includes('urlencoded')) {
191
217
  return Object.fromEntries(new URLSearchParams(text));
192
218
  }
193
-
219
+
194
220
  // Return raw text
195
221
  return text;
196
222
  } catch (e) {
@@ -203,28 +229,28 @@ export class ServerRouter {
203
229
  return new Promise((resolve, reject) => {
204
230
  const contentLengthHeader = req.headers['content-length'];
205
231
  const contentLength = parseInt(Array.isArray(contentLengthHeader) ? contentLengthHeader[0] : (contentLengthHeader || '0'), 10);
206
-
232
+
207
233
  if (contentLength === 0) {
208
234
  resolve({});
209
235
  return;
210
236
  }
211
237
 
212
238
  const chunks: Buffer[] = [];
213
-
239
+
214
240
  req.on('data', chunk => {
215
241
  chunks.push(Buffer.from(chunk));
216
242
  });
217
-
243
+
218
244
  req.on('end', () => {
219
245
  const body = Buffer.concat(chunks).toString();
220
246
  try {
221
247
  const ct = req.headers['content-type'] || '';
222
248
  resolve(ct.includes('json') ? (body ? JSON.parse(body) : {}) : ct.includes('urlencoded') ? Object.fromEntries(new URLSearchParams(body)) : body);
223
- } catch (e) {
224
- reject(e);
249
+ } catch (e) {
250
+ reject(e);
225
251
  }
226
252
  });
227
-
253
+
228
254
  req.on('error', reject);
229
255
  });
230
256
  }
@@ -240,48 +266,26 @@ export class ServerRouter {
240
266
 
241
267
  let body: any = {};
242
268
  if (['POST', 'PUT', 'PATCH'].includes(method)) {
243
- try {
269
+ try {
244
270
  body = await this.parseBody(req);
271
+ // Attach body to req for Express-like compatibility
272
+ (req as ElitRequest).body = body;
245
273
  }
246
- catch (e) {
247
- res.writeHead(400, { 'Content-Type': 'application/json' });
248
- res.end('{"error":"Invalid request body"}');
249
- return true;
274
+ catch (e) {
275
+ res.writeHead(400, { 'Content-Type': 'application/json' });
276
+ res.end('{"error":"Invalid request body"}');
277
+ return true;
250
278
  }
251
279
  }
252
280
 
253
281
  // Add Express-like response helpers to context
254
- const ctx: ServerRouteContext = {
255
- req,
256
- res,
257
- params,
258
- query: this.parseQuery(url),
259
- body,
260
- headers: req.headers as any,
261
- send: (data: any) => {
262
- if (!res.headersSent) {
263
- if (typeof data === 'object') {
264
- res.setHeader('Content-Type', 'application/json');
265
- res.end(JSON.stringify(data));
266
- } else {
267
- res.end(String(data));
268
- }
269
- }
270
- return res;
271
- },
272
- json: (data: any) => {
273
- if (!res.headersSent) {
274
- res.setHeader('Content-Type', 'application/json');
275
- res.end(JSON.stringify(data));
276
- }
277
- return res;
278
- },
279
- status: (code: number) => {
280
- if (!res.headersSent) {
281
- res.statusCode = code;
282
- }
283
- return res;
284
- }
282
+ const ctx: ServerRouteContext = {
283
+ req: req as ElitRequest,
284
+ res: res as ElitResponse,
285
+ params,
286
+ query: this.parseQuery(url),
287
+ body,
288
+ headers: req.headers as any
285
289
  };
286
290
 
287
291
  // Build middleware chain: global middlewares -> route middlewares -> final handler
@@ -300,8 +304,8 @@ export class ServerRouter {
300
304
  await mw(ctx, next);
301
305
  };
302
306
 
303
- try {
304
- await next();
307
+ try {
308
+ await next();
305
309
  }
306
310
  catch (e) {
307
311
  console.error('[ServerRouter] Route error:', e);
@@ -1074,7 +1078,7 @@ export class StateManager {
1074
1078
 
1075
1079
  // ===== Development Server =====
1076
1080
 
1077
- const defaultOptions: Omit<Required<DevServerOptions>, 'api' | 'clients' | 'root' | 'basePath' | 'ssr' | 'proxy' | 'index' | 'env'> = {
1081
+ const defaultOptions: Omit<Required<DevServerOptions>, 'api' | 'clients' | 'root' | 'basePath' | 'ssr' | 'proxy' | 'index' | 'env' | 'domain' | 'database'> = {
1078
1082
  port: 3000,
1079
1083
  host: 'localhost',
1080
1084
  https: false,
@@ -1106,6 +1110,14 @@ export function createDevServer(options: DevServerOptions): DevServer {
1106
1110
  clearImportMapCache();
1107
1111
  }
1108
1112
 
1113
+ // Initialize database connections if provided
1114
+ serverDatabase.initialize(config.database ? config.database : {
1115
+ dir: resolve(process.cwd(), 'databases')
1116
+ })
1117
+
1118
+
1119
+
1120
+
1109
1121
  // Normalize clients configuration - support both new API (clients array) and legacy API (root/basePath)
1110
1122
  const clientsToNormalize = config.clients?.length ? config.clients : config.root ? [{ root: config.root, basePath: config.basePath || '', index: config.index, ssr: config.ssr, api: config.api, proxy: config.proxy, mode: config.mode }] : null;
1111
1123
  if (!clientsToNormalize) throw new Error('DevServerOptions must include either "clients" array or "root" directory');
@@ -1146,6 +1158,19 @@ export function createDevServer(options: DevServerOptions): DevServer {
1146
1158
  // HTTP Server
1147
1159
  const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
1148
1160
  const originalUrl = req.url || '/';
1161
+ const hostHeader = req.headers.host;
1162
+ const hostName = hostHeader ? (Array.isArray(hostHeader) ? hostHeader[0] : hostHeader).split(':')[0] : '';
1163
+
1164
+ // Handle domain mapping: redirect localhost:port to configured domain
1165
+ if (config.domain && hostName === (config.host || 'localhost')) {
1166
+ const redirectUrl = `http://${config.domain}${originalUrl}`;
1167
+ if (config.logging) {
1168
+ console.log(`[Domain Map] ${hostName}:${config.port}${originalUrl} -> ${redirectUrl}`);
1169
+ }
1170
+ res.writeHead(302, { Location: redirectUrl });
1171
+ res.end();
1172
+ return;
1173
+ }
1149
1174
 
1150
1175
  // Find matching client based on basePath
1151
1176
  const matchedClient = normalizedClients.find(c => c.basePath && originalUrl.startsWith(c.basePath)) || normalizedClients.find(c => !c.basePath);
package/src/types.ts CHANGED
@@ -72,6 +72,7 @@ export type ElementFactory = {
72
72
 
73
73
  import type { Server } from 'http';
74
74
  import type { WebSocketServer } from 'ws';
75
+ import { DatabaseConfig } from './database';
75
76
 
76
77
  // Forward declarations to avoid circular dependency
77
78
  export type Router = import('./server').ServerRouter;
@@ -129,6 +130,8 @@ export interface DevServerOptions {
129
130
  port?: number;
130
131
  /** Host to bind to (default: 'localhost') */
131
132
  host?: string;
133
+ /** Domain to map (e.g., 'idevcoder.com') - redirects domain traffic to this server's port */
134
+ domain?: string;
132
135
  /** Root directory to serve files from */
133
136
  root?: string;
134
137
  /** Base path for the client application (e.g., '/app1', '/app2') */
@@ -159,6 +162,8 @@ export interface DevServerOptions {
159
162
  mode?: 'dev' | 'preview';
160
163
  /** Environment variables to inject (prefix with VITE_ for client access) */
161
164
  env?: Record<string, string>;
165
+ /** List of database directories to load */
166
+ database?: DatabaseConfig;
162
167
  }
163
168
 
164
169
  export interface DevServer {
@@ -239,6 +244,8 @@ export interface PreviewOptions {
239
244
  port?: number;
240
245
  /** Host to bind to (default: 'localhost') */
241
246
  host?: string;
247
+ /** Domain to map (e.g., 'idevcoder.com') - redirects domain traffic to this server's port */
248
+ domain?: string;
242
249
  /** Root directory to serve files from (default: dist or build.outDir) */
243
250
  root?: string;
244
251
  /** Base path for the application (e.g., '/app') */