baraqex 1.0.35 → 1.0.37

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.
Files changed (78) hide show
  1. package/dist/browser.js +635 -0
  2. package/dist/browser.js.map +7 -0
  3. package/dist/browser.package.json +4 -0
  4. package/dist/index.js +2024 -33
  5. package/dist/index.js.map +7 -1
  6. package/dist/server/index.js +1357 -432
  7. package/dist/server/index.js.map +7 -1
  8. package/dist/server/package.json +4 -0
  9. package/dist/wasm.js +86 -104
  10. package/dist/wasm.js.map +7 -1
  11. package/package.json +102 -86
  12. package/templates/ssr-template/esbuild.config.js +28 -3
  13. package/templates/ssr-template/package-lock.json +74 -1035
  14. package/templates/ssr-template/package.json +1 -0
  15. package/templates/ssr-template/src/App.tsx +11 -11
  16. package/templates/ssr-template/src/client.tsx +3 -2
  17. package/dist/frontend/components/WasmLoader.d.ts +0 -1
  18. package/dist/frontend/components/WasmLoader.d.ts.map +0 -1
  19. package/dist/frontend/components/WasmLoader.js +0 -2
  20. package/dist/frontend/components/WasmLoader.js.map +0 -1
  21. package/dist/frontend/hooks/useWasm.d.ts +0 -1
  22. package/dist/frontend/hooks/useWasm.d.ts.map +0 -1
  23. package/dist/frontend/hooks/useWasm.js +0 -2
  24. package/dist/frontend/hooks/useWasm.js.map +0 -1
  25. package/dist/frontend/index.d.ts +0 -1
  26. package/dist/frontend/index.d.ts.map +0 -1
  27. package/dist/frontend/index.js +0 -2
  28. package/dist/frontend/index.js.map +0 -1
  29. package/dist/index.d.ts +0 -12
  30. package/dist/index.d.ts.map +0 -1
  31. package/dist/renderComponent.d.ts +0 -9
  32. package/dist/renderComponent.d.ts.map +0 -1
  33. package/dist/renderComponent.js +0 -39
  34. package/dist/renderComponent.js.map +0 -1
  35. package/dist/server/api-router.d.ts +0 -16
  36. package/dist/server/api-router.d.ts.map +0 -1
  37. package/dist/server/api-router.js +0 -121
  38. package/dist/server/api-router.js.map +0 -1
  39. package/dist/server/auth.d.ts +0 -33
  40. package/dist/server/auth.d.ts.map +0 -1
  41. package/dist/server/auth.js +0 -74
  42. package/dist/server/auth.js.map +0 -1
  43. package/dist/server/database.d.ts +0 -25
  44. package/dist/server/database.d.ts.map +0 -1
  45. package/dist/server/database.js +0 -109
  46. package/dist/server/database.js.map +0 -1
  47. package/dist/server/index.d.ts +0 -117
  48. package/dist/server/index.d.ts.map +0 -1
  49. package/dist/server/middleware.d.ts +0 -12
  50. package/dist/server/middleware.d.ts.map +0 -1
  51. package/dist/server/middleware.js +0 -46
  52. package/dist/server/middleware.js.map +0 -1
  53. package/dist/server/server.d.ts +0 -10
  54. package/dist/server/server.d.ts.map +0 -1
  55. package/dist/server/server.js +0 -81
  56. package/dist/server/server.js.map +0 -1
  57. package/dist/server/templates.d.ts +0 -31
  58. package/dist/server/templates.d.ts.map +0 -1
  59. package/dist/server/templates.js +0 -208
  60. package/dist/server/templates.js.map +0 -1
  61. package/dist/server/types.d.ts +0 -67
  62. package/dist/server/types.d.ts.map +0 -1
  63. package/dist/server/types.js +0 -2
  64. package/dist/server/types.js.map +0 -1
  65. package/dist/server/utils.d.ts +0 -71
  66. package/dist/server/utils.d.ts.map +0 -1
  67. package/dist/server/utils.js +0 -165
  68. package/dist/server/utils.js.map +0 -1
  69. package/dist/server/wasm.d.ts +0 -29
  70. package/dist/server/wasm.d.ts.map +0 -1
  71. package/dist/server/wasm.js +0 -208
  72. package/dist/server/wasm.js.map +0 -1
  73. package/dist/server-renderer.d.ts +0 -5
  74. package/dist/server-renderer.d.ts.map +0 -1
  75. package/dist/server-renderer.js +0 -27
  76. package/dist/server-renderer.js.map +0 -1
  77. package/dist/wasm.d.ts +0 -51
  78. package/dist/wasm.d.ts.map +0 -1
@@ -1,468 +1,1393 @@
1
- import express from 'express';
2
- import path from 'path';
3
- import fs from 'fs';
4
- import cors from 'cors';
5
- import { renderToString } from 'frontend-hamroun';
6
- import { Database } from './database.js';
7
- import { AuthService } from './auth.js';
8
- import { ApiRouter } from './api-router.js';
9
- import { requestLogger, errorHandler, notFoundHandler, rateLimit } from './middleware.js';
10
- // Import the utility functions
11
- import * as utils from './utils.js';
12
- import * as templates from './templates.js';
13
- import { loadGoWasmFromFile } from './wasm.js';
14
- // Helper function to get the component name from the file path
15
- function getComponentName(filePath, pagesDir) {
16
- try {
17
- // Get relative path from pages directory
18
- const relativePath = path.relative(pagesDir, filePath);
19
- // Remove extension
20
- const withoutExt = relativePath.replace(/\.[^/.]+$/, '');
21
- // Replace index with empty for clean paths
22
- const cleanPath = withoutExt.replace(/\/index$/, '');
23
- // Convert to module path format
24
- return `/pages/${cleanPath}`;
25
- }
26
- catch (e) {
27
- return '';
28
- }
1
+ var __defProp = Object.defineProperty;
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined")
6
+ return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+
14
+ // src/server/index.ts
15
+ import path4 from "path";
16
+ import fs4 from "fs";
17
+
18
+ // node_modules/frontend-hamroun/dist/index.mjs
19
+ var currentRender = 0;
20
+ var stateIndices = /* @__PURE__ */ new Map();
21
+ var isServer = typeof window === "undefined";
22
+ var serverStates = /* @__PURE__ */ new Map();
23
+ function prepareRender() {
24
+ currentRender++;
25
+ stateIndices.set(currentRender, 0);
26
+ return currentRender;
29
27
  }
30
- export class Server {
31
- constructor(config = {}) {
32
- this.server = null;
33
- this.db = null;
34
- this.auth = null;
35
- this.config = {
36
- port: 3000,
37
- apiDir: './api',
38
- pagesDir: './pages',
39
- staticDir: './public',
40
- enableCors: true,
41
- ...config
42
- };
43
- this.app = express();
44
- // Basic middleware
45
- this.app.use(express.json());
46
- this.app.use(express.urlencoded({ extended: true }));
47
- // CORS setup
48
- if (this.config.enableCors) {
49
- this.app.use(cors(this.config.corsOptions));
50
- }
51
- // Setup database if configured
52
- if (this.config.db) {
53
- this.db = new Database(this.config.db);
54
- }
55
- // Setup auth if configured
56
- if (this.config.auth) {
57
- this.auth = new AuthService({
58
- secret: this.config.auth.secret,
59
- expiresIn: this.config.auth.expiresIn
60
- });
61
- this.app.use(this.auth.initialize());
28
+ function finishRender() {
29
+ if (isServer) {
30
+ serverStates.delete(currentRender);
31
+ }
32
+ currentRender = 0;
33
+ }
34
+ function renderToString(vnode) {
35
+ prepareRender();
36
+ const html = renderNodeToString(vnode);
37
+ finishRender();
38
+ return html;
39
+ }
40
+ function renderNodeToString(vnode) {
41
+ var _a, _b, _c;
42
+ if (vnode === null || vnode === void 0)
43
+ return "";
44
+ if (typeof vnode === "boolean")
45
+ return "";
46
+ if (typeof vnode === "number" || typeof vnode === "string")
47
+ return escapeHtml(String(vnode));
48
+ if (typeof vnode.type === "function") {
49
+ const Component2 = vnode.type;
50
+ const renderedNode = Component2(vnode.props || {});
51
+ return renderNodeToString(renderedNode);
52
+ }
53
+ if (typeof vnode.type === "string") {
54
+ const tag = vnode.type;
55
+ let attrs = "";
56
+ let children = "";
57
+ if (vnode.props) {
58
+ for (const [key, value] of Object.entries(vnode.props)) {
59
+ if (key === "children")
60
+ continue;
61
+ if (key.startsWith("on"))
62
+ continue;
63
+ if (key === "className") {
64
+ attrs += ` class="${escapeHtml(value)}"`;
65
+ continue;
62
66
  }
63
- // Static files - setup before other routes
64
- const staticPath = path.resolve(process.cwd(), this.config.staticDir);
65
- if (fs.existsSync(staticPath)) {
66
- this.app.use(express.static(staticPath));
67
+ if (typeof value === "string" || typeof value === "number") {
68
+ attrs += ` ${key}="${escapeHtml(String(value))}"`;
69
+ } else if (value === true) {
70
+ attrs += ` ${key}`;
67
71
  }
68
- // Setup API routes
69
- this.setupApiRoutes();
70
- // Setup page routes (for SSR)
71
- this.setupPageRoutes();
72
- // Add error handlers at the end
73
- this.app.use(notFoundHandler);
74
- this.app.use(errorHandler);
75
- }
76
- setupApiRoutes() {
77
- const apiRouter = new ApiRouter(this.config.apiDir, this.auth);
78
- this.app.use('/api', requestLogger, apiRouter.router);
79
- // Add API documentation route
80
- this.app.get('/api-docs', (req, res) => {
81
- const apiDocs = this.generateApiDocs();
82
- res.json(apiDocs);
83
- });
72
+ }
84
73
  }
85
- generateApiDocs() {
86
- // Generate documentation based on API routes
87
- const apiDir = path.resolve(process.cwd(), this.config.apiDir);
88
- const docs = { endpoints: [] };
89
- if (fs.existsSync(apiDir)) {
90
- this.scanApiDirectory(apiDir, '', docs.endpoints);
91
- }
92
- return docs;
93
- }
94
- scanApiDirectory(dirPath, routePrefix, endpoints) {
95
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
96
- for (const entry of entries) {
97
- const fullPath = path.join(dirPath, entry.name);
98
- if (entry.isDirectory()) {
99
- this.scanApiDirectory(fullPath, `${routePrefix}/${entry.name}`, endpoints);
100
- }
101
- else if (entry.name.endsWith('.js') || entry.name.endsWith('.ts')) {
102
- const routePath = this.getRoutePath(entry.name, routePrefix);
103
- endpoints.push({
104
- path: routePath,
105
- file: path.relative(process.cwd(), fullPath),
106
- methods: this.detectApiMethods(fullPath)
107
- });
108
- }
74
+ const childrenArray = ((_a = vnode.props) == null ? void 0 : _a.children) ? Array.isArray(vnode.props.children) ? vnode.props.children : [vnode.props.children] : [];
75
+ for (const child of childrenArray) {
76
+ children += renderNodeToString(child);
77
+ }
78
+ const selfClosing = [
79
+ "area",
80
+ "base",
81
+ "br",
82
+ "col",
83
+ "embed",
84
+ "hr",
85
+ "img",
86
+ "input",
87
+ "link",
88
+ "meta",
89
+ "param",
90
+ "source",
91
+ "track",
92
+ "wbr"
93
+ ];
94
+ if (selfClosing.includes(tag)) {
95
+ return `<${tag}${attrs}/>`;
96
+ }
97
+ return `<${tag}${attrs}>${children}</${tag}>`;
98
+ }
99
+ if (vnode.type === Symbol.for("react.fragment")) {
100
+ let fragmentOutput = "";
101
+ const children = Array.isArray((_b = vnode.props) == null ? void 0 : _b.children) ? vnode.props.children : ((_c = vnode.props) == null ? void 0 : _c.children) ? [vnode.props.children] : [];
102
+ for (const child of children) {
103
+ fragmentOutput += renderNodeToString(child);
104
+ }
105
+ return fragmentOutput;
106
+ }
107
+ console.warn("Unknown vnode type:", vnode.type);
108
+ return "";
109
+ }
110
+ function escapeHtml(text) {
111
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
112
+ }
113
+
114
+ // src/server/database.ts
115
+ var Database = class {
116
+ constructor(config) {
117
+ this.mongoClient = null;
118
+ this.mongoDb = null;
119
+ this.mysqlPool = null;
120
+ this.pgPool = null;
121
+ this.config = config;
122
+ }
123
+ async connect() {
124
+ switch (this.config.type) {
125
+ case "mongodb":
126
+ await this.connectMongo();
127
+ break;
128
+ case "mysql":
129
+ await this.connectMysql();
130
+ break;
131
+ case "postgres":
132
+ await this.connectPostgres();
133
+ break;
134
+ default:
135
+ throw new Error(`Unsupported database type: ${this.config.type}`);
136
+ }
137
+ }
138
+ async connectMongo() {
139
+ try {
140
+ const { MongoClient } = await import("mongodb");
141
+ this.mongoClient = new MongoClient(this.config.url);
142
+ await this.mongoClient.connect();
143
+ this.mongoDb = this.mongoClient.db();
144
+ console.log("Connected to MongoDB");
145
+ } catch (error) {
146
+ if (error.code === "MODULE_NOT_FOUND") {
147
+ throw new Error("MongoDB driver not installed. Run: npm install mongodb");
148
+ }
149
+ console.error("MongoDB connection error:", error);
150
+ throw error;
151
+ }
152
+ }
153
+ async connectMysql() {
154
+ try {
155
+ const mysql = await import("mysql2/promise");
156
+ this.mysqlPool = mysql.createPool(this.config.url);
157
+ await this.mysqlPool.getConnection();
158
+ console.log("Connected to MySQL");
159
+ } catch (error) {
160
+ if (error.code === "MODULE_NOT_FOUND") {
161
+ throw new Error("MySQL driver not installed. Run: npm install mysql2");
162
+ }
163
+ console.error("MySQL connection error:", error);
164
+ throw error;
165
+ }
166
+ }
167
+ async connectPostgres() {
168
+ try {
169
+ const pg = await import("pg");
170
+ this.pgPool = new pg.Pool({ connectionString: this.config.url });
171
+ const client = await this.pgPool.connect();
172
+ client.release();
173
+ console.log("Connected to PostgreSQL");
174
+ } catch (error) {
175
+ if (error.code === "MODULE_NOT_FOUND") {
176
+ throw new Error("PostgreSQL driver not installed. Run: npm install pg @types/pg");
177
+ }
178
+ console.error("PostgreSQL connection error:", error);
179
+ throw error;
180
+ }
181
+ }
182
+ async disconnect() {
183
+ if (this.mongoClient) {
184
+ await this.mongoClient.close();
185
+ this.mongoClient = null;
186
+ this.mongoDb = null;
187
+ }
188
+ if (this.mysqlPool) {
189
+ await this.mysqlPool.end();
190
+ this.mysqlPool = null;
191
+ }
192
+ if (this.pgPool) {
193
+ await this.pgPool.end();
194
+ this.pgPool = null;
195
+ }
196
+ }
197
+ // Database-specific methods
198
+ getMongoDb() {
199
+ return this.mongoDb;
200
+ }
201
+ getMysqlPool() {
202
+ return this.mysqlPool;
203
+ }
204
+ getPgPool() {
205
+ return this.pgPool;
206
+ }
207
+ // Generic query method
208
+ async query(sql, values) {
209
+ switch (this.config.type) {
210
+ case "mongodb":
211
+ throw new Error("For MongoDB, use getMongoDb() instead of query()");
212
+ case "mysql":
213
+ if (!this.mysqlPool)
214
+ throw new Error("MySQL not connected");
215
+ const [rows] = await this.mysqlPool.execute(sql, values || []);
216
+ return rows;
217
+ case "postgres":
218
+ if (!this.pgPool)
219
+ throw new Error("PostgreSQL not connected");
220
+ const result = await this.pgPool.query(sql, values || []);
221
+ return result.rows;
222
+ default:
223
+ throw new Error(`Unsupported database type: ${this.config.type}`);
224
+ }
225
+ }
226
+ };
227
+
228
+ // src/server/auth.ts
229
+ var AuthService = class {
230
+ constructor(config) {
231
+ this.config = {
232
+ expiresIn: "24h",
233
+ ...config
234
+ };
235
+ }
236
+ async hashPassword(password) {
237
+ try {
238
+ const bcrypt = await import("bcryptjs");
239
+ return bcrypt.hash(password, 10);
240
+ } catch (error) {
241
+ if (error.code === "MODULE_NOT_FOUND") {
242
+ throw new Error("bcryptjs not installed. Run: npm install bcryptjs @types/bcryptjs");
243
+ }
244
+ throw error;
245
+ }
246
+ }
247
+ async comparePasswords(password, hashedPassword) {
248
+ try {
249
+ const bcrypt = await import("bcryptjs");
250
+ return bcrypt.compare(password, hashedPassword);
251
+ } catch (error) {
252
+ if (error.code === "MODULE_NOT_FOUND") {
253
+ throw new Error("bcryptjs not installed. Run: npm install bcryptjs @types/bcryptjs");
254
+ }
255
+ throw error;
256
+ }
257
+ }
258
+ generateToken(user) {
259
+ try {
260
+ const jwt = __require("jsonwebtoken");
261
+ const options = {};
262
+ if (this.config.expiresIn) {
263
+ options.expiresIn = this.config.expiresIn;
264
+ }
265
+ return jwt.sign(
266
+ { id: user.id, username: user.username, roles: user.roles || [] },
267
+ this.config.secret,
268
+ options
269
+ );
270
+ } catch (error) {
271
+ if (error.code === "MODULE_NOT_FOUND") {
272
+ throw new Error("jsonwebtoken not installed. Run: npm install jsonwebtoken @types/jsonwebtoken");
273
+ }
274
+ throw error;
275
+ }
276
+ }
277
+ verifyToken(token) {
278
+ try {
279
+ const jwt = __require("jsonwebtoken");
280
+ return jwt.verify(token, this.config.secret);
281
+ } catch (error) {
282
+ if (error.code === "MODULE_NOT_FOUND") {
283
+ throw new Error("jsonwebtoken not installed. Run: npm install jsonwebtoken @types/jsonwebtoken");
284
+ }
285
+ return null;
286
+ }
287
+ }
288
+ // Express middleware for authentication
289
+ initialize() {
290
+ return (req, res, next) => {
291
+ const token = this.extractToken(req);
292
+ if (token) {
293
+ const decoded = this.verifyToken(token);
294
+ if (decoded) {
295
+ req.user = decoded;
109
296
  }
297
+ }
298
+ next();
299
+ };
300
+ }
301
+ // Express middleware for requiring authentication
302
+ requireAuth() {
303
+ return (req, res, next) => {
304
+ if (!req.user) {
305
+ return res.status(401).json({ message: "Unauthorized" });
306
+ }
307
+ return next();
308
+ };
309
+ }
310
+ // Express middleware for requiring specific roles
311
+ requireRoles(roles) {
312
+ return (req, res, next) => {
313
+ if (!req.user) {
314
+ return res.status(401).json({ message: "Unauthorized" });
315
+ }
316
+ const userRoles = req.user.roles || [];
317
+ const hasRequiredRole = roles.some((role) => userRoles.includes(role));
318
+ if (!hasRequiredRole) {
319
+ return res.status(403).json({ message: "Forbidden" });
320
+ }
321
+ return next();
322
+ };
323
+ }
324
+ extractToken(req) {
325
+ if (req.headers.authorization && req.headers.authorization.startsWith("Bearer ")) {
326
+ return req.headers.authorization.substring(7);
327
+ }
328
+ return null;
329
+ }
330
+ };
331
+
332
+ // src/server/api-router.ts
333
+ import path from "path";
334
+ import fs from "fs";
335
+ import { pathToFileURL } from "url";
336
+ var ApiRouter = class {
337
+ constructor(apiDir, auth = null) {
338
+ this.router = null;
339
+ this.apiDir = path.resolve(process.cwd(), apiDir);
340
+ this.auth = auth;
341
+ this.initializeRouter();
342
+ }
343
+ async initializeRouter() {
344
+ try {
345
+ const express = await import("express");
346
+ this.router = express.default.Router();
347
+ this.setupRoutes();
348
+ } catch (error) {
349
+ if (error.code === "MODULE_NOT_FOUND") {
350
+ throw new Error("Express not installed. Run: npm install express @types/express");
351
+ }
352
+ throw error;
353
+ }
354
+ }
355
+ setupRoutes() {
356
+ if (!fs.existsSync(this.apiDir)) {
357
+ console.log(`API directory not found: ${this.apiDir}`);
358
+ return;
359
+ }
360
+ this.scanDirectory(this.apiDir, "");
361
+ }
362
+ scanDirectory(dirPath, routePrefix) {
363
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
364
+ for (const entry of entries) {
365
+ const fullPath = path.join(dirPath, entry.name);
366
+ if (entry.isDirectory()) {
367
+ this.scanDirectory(fullPath, `${routePrefix}/${entry.name}`);
368
+ } else if (this.isRouteFile(entry.name)) {
369
+ const routePath = this.getRoutePath(entry.name, routePrefix);
370
+ this.registerRoute(fullPath, routePath);
371
+ }
372
+ }
373
+ }
374
+ isRouteFile(filename) {
375
+ return filename.endsWith(".js") || filename.endsWith(".ts") || filename.endsWith(".mjs");
376
+ }
377
+ getRoutePath(filename, routePrefix) {
378
+ const baseName = path.basename(filename, path.extname(filename));
379
+ if (baseName === "index") {
380
+ return routePrefix || "/";
381
+ }
382
+ const paramMatch = baseName.match(/^\[(.+)\]$/);
383
+ if (paramMatch) {
384
+ return `${routePrefix}/:${paramMatch[1]}`;
110
385
  }
111
- getRoutePath(filename, routePrefix) {
112
- const baseName = path.basename(filename, path.extname(filename));
113
- if (baseName === 'index') {
114
- return routePrefix || '/';
386
+ return `${routePrefix}/${baseName}`;
387
+ }
388
+ async registerRoute(filePath, routePath) {
389
+ try {
390
+ const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(filePath);
391
+ const fileUrl = pathToFileURL(absolutePath).href;
392
+ if (process.platform === "win32") {
393
+ const urlWithTimestamp = `${fileUrl}?t=${Date.now()}`;
394
+ const routeModule = await import(urlWithTimestamp);
395
+ if (!this.isValidRouteModule(routeModule)) {
396
+ console.warn(`Invalid route module: ${filePath}`);
397
+ return;
115
398
  }
116
- const paramMatch = baseName.match(/^\[(.+)\]$/);
117
- if (paramMatch) {
118
- return `${routePrefix}/:${paramMatch[1]}`;
399
+ console.log(`Registering API route: ${routePath}`);
400
+ const middleware = routeModule.middleware || [];
401
+ for (const method of ["get", "post", "put", "delete", "patch", "options"]) {
402
+ const handler = routeModule[method];
403
+ if (typeof handler === "function") {
404
+ const routerMethod = this.router[method];
405
+ routerMethod.call(
406
+ this.router,
407
+ routePath,
408
+ ...middleware,
409
+ this.wrapHandler(handler)
410
+ );
411
+ }
119
412
  }
120
- return `${routePrefix}/${baseName}`;
121
- }
122
- detectApiMethods(filePath) {
123
- try {
124
- const content = fs.readFileSync(filePath, 'utf8');
125
- const methods = ['get', 'post', 'put', 'delete', 'patch', 'options'];
126
- return methods.filter(method => new RegExp(`export (async )?function ${method}\\s*\\(`).test(content) ||
127
- new RegExp(`export const ${method}\\s*=`).test(content));
413
+ } else {
414
+ const routeModule = await import(fileUrl);
415
+ if (!this.isValidRouteModule(routeModule)) {
416
+ console.warn(`Invalid route module: ${filePath}`);
417
+ return;
128
418
  }
129
- catch (error) {
130
- return [];
419
+ console.log(`Registering API route: ${routePath}`);
420
+ const middleware = routeModule.middleware || [];
421
+ for (const method of ["get", "post", "put", "delete", "patch", "options"]) {
422
+ const handler = routeModule[method];
423
+ if (typeof handler === "function") {
424
+ const routerMethod = this.router[method];
425
+ routerMethod.call(
426
+ this.router,
427
+ routePath,
428
+ ...middleware,
429
+ this.wrapHandler(handler)
430
+ );
431
+ }
131
432
  }
433
+ }
434
+ } catch (error) {
435
+ console.error(`Error registering route (${filePath}):`, error);
132
436
  }
133
- setupPageRoutes() {
134
- const pagesPath = path.resolve(process.cwd(), this.config.pagesDir);
135
- if (!fs.existsSync(pagesPath)) {
136
- return;
137
- }
138
- // Setup catch-all route for SSR - but only if pages directory exists
139
- this.app.get('*', async (req, res) => {
140
- try {
141
- const { html, statusCode } = await this.renderPage(req.path);
142
- res.status(statusCode || 200).send(html);
143
- }
144
- catch (error) {
145
- console.error('Error rendering page:', error);
146
- const errorHtml = templates.generateErrorPage(500, 'Server Error');
147
- res.status(500).send(errorHtml);
148
- }
149
- });
437
+ }
438
+ isValidRouteModule(module) {
439
+ if (!module)
440
+ return false;
441
+ return ["get", "post", "put", "delete", "patch", "options"].some((method) => typeof module[method] === "function");
442
+ }
443
+ wrapHandler(handler) {
444
+ return async (req, res, next) => {
445
+ try {
446
+ await handler(req, res, next);
447
+ } catch (error) {
448
+ next(error);
449
+ }
450
+ };
451
+ }
452
+ };
453
+
454
+ // src/server/middleware.ts
455
+ var requestLogger = (req, res, next) => {
456
+ console.log(`${(/* @__PURE__ */ new Date()).toISOString()} - ${req.method} ${req.url}`);
457
+ next();
458
+ };
459
+ var errorHandler = (err, req, res, next) => {
460
+ console.error(err.stack);
461
+ res.status(500).json({
462
+ error: {
463
+ message: "Internal Server Error",
464
+ ...process.env.NODE_ENV !== "production" ? { details: err.message, stack: err.stack } : {}
150
465
  }
151
- async renderPage(routePath) {
152
- try {
153
- // Try to find the page component
154
- const pagesDir = this.config.pagesDir ? path.resolve(process.cwd(), this.config.pagesDir) : '';
155
- if (!pagesDir || !fs.existsSync(pagesDir)) {
156
- return {
157
- html: templates.generateErrorPage(404, 'Pages directory not found'),
158
- statusCode: 404
159
- };
160
- }
161
- // Normalize the path
162
- let normalizedPath = routePath;
163
- if (!normalizedPath.startsWith('/')) {
164
- normalizedPath = '/' + normalizedPath;
165
- }
166
- // Default to index for the root
167
- if (normalizedPath === '/') {
168
- normalizedPath = '/index';
169
- }
170
- // Try to find a matching page file
171
- let pagePath = '';
172
- const possiblePaths = [
173
- path.join(pagesDir, `${normalizedPath}.js`),
174
- path.join(pagesDir, `${normalizedPath}.ts`),
175
- path.join(pagesDir, `${normalizedPath}.jsx`),
176
- path.join(pagesDir, `${normalizedPath}.tsx`),
177
- path.join(pagesDir, `${normalizedPath}/index.js`),
178
- path.join(pagesDir, `${normalizedPath}/index.ts`),
179
- path.join(pagesDir, `${normalizedPath}/index.jsx`),
180
- path.join(pagesDir, `${normalizedPath}/index.tsx`)
181
- ];
182
- for (const p of possiblePaths) {
183
- if (fs.existsSync(p)) {
184
- pagePath = p;
185
- break;
186
- }
187
- }
188
- // Handle 404 if page not found
189
- if (!pagePath) {
190
- return {
191
- html: templates.generateErrorPage(404, `Page not found: ${normalizedPath}`),
192
- statusCode: 404
193
- };
194
- }
195
- // Import and render the page component
196
- try {
197
- const pageModule = await import(pagePath);
198
- if (!pageModule || !pageModule.default) {
199
- throw new Error(`No default export found in ${pagePath}`);
200
- }
201
- const PageComponent = pageModule.default;
202
- const initialProps = {
203
- // Provide any initial props here
204
- path: normalizedPath,
205
- query: {}, // Could be parsed from URL
206
- api: { serverTime: new Date().toISOString() }
207
- };
208
- // Render the component to HTML
209
- const { html, success, error } = await renderComponent(PageComponent, initialProps);
210
- if (!success) {
211
- return {
212
- html: templates.generateErrorPage(500, 'Failed to render page', error instanceof Error ? error : new Error('Unknown error')),
213
- statusCode: 500
214
- };
215
- }
216
- // Generate the full HTML document
217
- let pageTitle = 'My App';
218
- try {
219
- // Try to extract title from component if it has a getTitle method
220
- if (typeof PageComponent.getTitle === 'function') {
221
- pageTitle = PageComponent.getTitle(initialProps);
222
- }
223
- else if (PageComponent.title) {
224
- pageTitle = PageComponent.title;
225
- }
226
- }
227
- catch (e) {
228
- // Ignore title errors
229
- }
230
- // Parse the component name for hydration
231
- const componentName = getComponentName(pagePath, pagesDir);
232
- // Generate full HTML document with our template
233
- const fullHtml = templates.generateDocument(html, {
234
- title: pageTitle,
235
- // Get description from component if available
236
- description: typeof PageComponent.getDescription === 'function'
237
- ? PageComponent.getDescription(initialProps)
238
- : (PageComponent.description || ''),
239
- // Add scripts for client-side hydration
240
- scripts: ['/client.js'],
241
- // Add any custom meta tags
242
- meta: typeof PageComponent.getMeta === 'function'
243
- ? PageComponent.getMeta(initialProps)
244
- : {},
245
- // Add custom styles
246
- styles: ['/styles.css'],
247
- // Add initial data for client-side hydration
248
- initialData: initialProps,
249
- // Add component information for hydration
250
- componentName: componentName
251
- });
252
- return { html: fullHtml, statusCode: 200 };
253
- }
254
- catch (error) {
255
- console.error('Error rendering page:', error);
256
- return {
257
- html: templates.generateErrorPage(500, 'Error rendering page', error instanceof Error ? error : new Error('Unknown error')),
258
- statusCode: 500
259
- };
260
- }
261
- }
262
- catch (error) {
263
- console.error('Error finding page:', error);
264
- return {
265
- html: templates.generateErrorPage(500, 'Server error', error instanceof Error ? error : new Error('Unknown error')),
266
- statusCode: 500
267
- };
466
+ });
467
+ };
468
+ var notFoundHandler = (req, res) => {
469
+ res.status(404).json({
470
+ error: {
471
+ message: `Not Found - ${req.method} ${req.url}`
472
+ }
473
+ });
474
+ };
475
+ function rateLimit(options) {
476
+ const requests = /* @__PURE__ */ new Map();
477
+ return (req, res, next) => {
478
+ const ip = req.ip || req.socket.remoteAddress || "unknown";
479
+ const now = Date.now();
480
+ const reqTimes = (requests.get(ip) || []).filter((time) => now - time < options.windowMs);
481
+ reqTimes.push(now);
482
+ requests.set(ip, reqTimes);
483
+ if (reqTimes.length > options.max) {
484
+ return res.status(429).json({
485
+ error: {
486
+ message: "Too Many Requests",
487
+ retryAfter: Math.ceil(options.windowMs / 1e3)
268
488
  }
489
+ });
490
+ }
491
+ return next();
492
+ };
493
+ }
494
+
495
+ // src/server/utils.ts
496
+ var utils_exports = {};
497
+ __export(utils_exports, {
498
+ ensureDirectory: () => ensureDirectory,
499
+ generateToken: () => generateToken,
500
+ getEnvironmentInfo: () => getEnvironmentInfo,
501
+ getPagination: () => getPagination,
502
+ hashString: () => hashString,
503
+ isDirectoryEmpty: () => isDirectoryEmpty,
504
+ readJsonFile: () => readJsonFile,
505
+ safeJsonParse: () => safeJsonParse,
506
+ sendError: () => sendError,
507
+ sendSuccess: () => sendSuccess,
508
+ validateFields: () => validateFields,
509
+ validateFileUpload: () => validateFileUpload,
510
+ writeJsonFile: () => writeJsonFile
511
+ });
512
+ import crypto from "crypto";
513
+ import fs2 from "fs/promises";
514
+ import path2 from "path";
515
+ function safeJsonParse(json, fallback) {
516
+ try {
517
+ return JSON.parse(json);
518
+ } catch (error) {
519
+ return fallback;
520
+ }
521
+ }
522
+ function generateToken(length = 32) {
523
+ return crypto.randomBytes(length).toString("hex");
524
+ }
525
+ function hashString(input, salt = "") {
526
+ return crypto.createHash("sha256").update(input + salt).digest("hex");
527
+ }
528
+ function getPagination(req) {
529
+ const page = Math.max(1, parseInt(req.query.page) || 1);
530
+ const rawLimit = parseInt(req.query.limit);
531
+ let limit;
532
+ if (isNaN(rawLimit) || rawLimit <= 0) {
533
+ limit = 20;
534
+ } else {
535
+ limit = Math.max(1, Math.min(100, rawLimit));
536
+ }
537
+ const skip = (page - 1) * limit;
538
+ return { page, limit, skip };
539
+ }
540
+ function sendSuccess(res, data, message = "Success") {
541
+ return res.json({
542
+ success: true,
543
+ message,
544
+ data
545
+ });
546
+ }
547
+ function sendError(res, message = "An error occurred", statusCode = 400, errors) {
548
+ return res.status(statusCode).json({
549
+ success: false,
550
+ message,
551
+ errors: errors || void 0
552
+ });
553
+ }
554
+ function validateFields(req, requiredFields) {
555
+ const missing = requiredFields.filter((field) => {
556
+ const value = req.body[field];
557
+ return value === void 0 || value === null || value === "";
558
+ });
559
+ return {
560
+ valid: missing.length === 0,
561
+ missing
562
+ };
563
+ }
564
+ function validateFileUpload(file, options = {}) {
565
+ if (!file) {
566
+ return { valid: false, error: "No file provided" };
567
+ }
568
+ if (options.maxSize && file.size > options.maxSize) {
569
+ return {
570
+ valid: false,
571
+ error: `File too large. Maximum size allowed is ${options.maxSize / 1024 / 1024}MB`
572
+ };
573
+ }
574
+ if (options.allowedTypes && options.allowedTypes.length > 0) {
575
+ const fileType = file.mimetype;
576
+ if (!options.allowedTypes.includes(fileType)) {
577
+ return {
578
+ valid: false,
579
+ error: `Invalid file type. Allowed types: ${options.allowedTypes.join(", ")}`
580
+ };
581
+ }
582
+ }
583
+ return { valid: true };
584
+ }
585
+ function getEnvironmentInfo() {
586
+ return {
587
+ nodeVersion: process.version,
588
+ platform: process.platform,
589
+ arch: process.arch,
590
+ memory: process.memoryUsage(),
591
+ uptime: process.uptime(),
592
+ env: process.env.NODE_ENV || "development"
593
+ };
594
+ }
595
+ async function isDirectoryEmpty(dirPath) {
596
+ try {
597
+ const files = await fs2.readdir(dirPath);
598
+ return files.length === 0;
599
+ } catch (error) {
600
+ return true;
601
+ }
602
+ }
603
+ async function ensureDirectory(dirPath) {
604
+ try {
605
+ await fs2.access(dirPath);
606
+ } catch (error) {
607
+ await fs2.mkdir(dirPath, { recursive: true });
608
+ }
609
+ }
610
+ async function writeJsonFile(filePath, data) {
611
+ await ensureDirectory(path2.dirname(filePath));
612
+ await fs2.writeFile(filePath, JSON.stringify(data, null, 2), "utf8");
613
+ }
614
+ async function readJsonFile(filePath, defaultValue) {
615
+ try {
616
+ const data = await fs2.readFile(filePath, "utf8");
617
+ return safeJsonParse(data, defaultValue);
618
+ } catch (error) {
619
+ return defaultValue;
620
+ }
621
+ }
622
+
623
+ // src/server/templates.ts
624
+ var templates_exports = {};
625
+ __export(templates_exports, {
626
+ generateDocument: () => generateDocument,
627
+ generateErrorPage: () => generateErrorPage,
628
+ generateLoadingPage: () => generateLoadingPage
629
+ });
630
+ function generateDocument(content, options = {}) {
631
+ const {
632
+ title = "Frontend Hamroun App",
633
+ description = "",
634
+ lang = "en",
635
+ charset = "UTF-8",
636
+ viewport = "width=device-width, initial-scale=1.0",
637
+ scripts = [],
638
+ styles = [],
639
+ meta = {},
640
+ bodyClasses = "",
641
+ htmlAttrs = {},
642
+ bodyAttrs = {},
643
+ initialData = {},
644
+ componentName = ""
645
+ } = options;
646
+ const htmlAttributes = Object.entries(htmlAttrs).map(([key, value]) => `${key}="${escapeHtml2(value)}"`).join(" ");
647
+ const bodyAttributes = Object.entries(bodyAttrs).map(([key, value]) => `${key}="${escapeHtml2(value)}"`).join(" ");
648
+ const metaTags = [
649
+ `<meta charset="${charset}">`,
650
+ `<meta name="viewport" content="${viewport}">`,
651
+ description ? `<meta name="description" content="${escapeHtml2(description)}">` : ""
652
+ ];
653
+ Object.entries(meta).forEach(([name, content2]) => {
654
+ metaTags.push(`<meta name="${escapeHtml2(name)}" content="${escapeHtml2(content2)}">`);
655
+ });
656
+ const scriptTags = scripts.map((src) => {
657
+ if (src.startsWith("<script")) {
658
+ return src;
269
659
  }
270
- getExpressApp() {
271
- return this.app;
660
+ return `<script src="${escapeHtml2(src)}" ${src.endsWith(".js") ? 'type="module"' : ""} defer></script>`;
661
+ });
662
+ const styleTags = styles.map((href) => {
663
+ if (href.startsWith("<style")) {
664
+ return href;
272
665
  }
273
- getDatabase() {
274
- return this.db;
666
+ return `<link rel="stylesheet" href="${escapeHtml2(href)}">`;
667
+ });
668
+ const initialDataScript = Object.keys(initialData).length > 0 ? `<script id="__APP_DATA__" type="application/json">${escapeHtml2(JSON.stringify(initialData))}</script>` : "";
669
+ return `<!DOCTYPE html>
670
+ <html lang="${lang}" ${htmlAttributes}>
671
+ <head>
672
+ ${metaTags.filter(Boolean).join("\n ")}
673
+ <title>${escapeHtml2(title)}</title>
674
+ ${styleTags.join("\n ")}
675
+ </head>
676
+ <body class="${bodyClasses}" ${bodyAttributes}>
677
+ <div id="app-root" data-ssr-root${componentName ? ` data-component="${escapeHtml2(componentName)}"` : ""}>${content}</div>
678
+ ${initialDataScript}
679
+ ${scriptTags.join("\n ")}
680
+ </body>
681
+ </html>`;
682
+ }
683
+ function generateErrorPage(statusCode, message, error) {
684
+ const errorMessages = {
685
+ 400: "Bad Request",
686
+ 401: "Unauthorized",
687
+ 403: "Forbidden",
688
+ 404: "Not Found",
689
+ 500: "Internal Server Error"
690
+ };
691
+ const title = `${statusCode} - ${errorMessages[statusCode] || "Error"}`;
692
+ const isDev = process.env.NODE_ENV !== "production";
693
+ const errorDetails = isDev && error ? `
694
+ <div class="error-details">
695
+ <pre>${escapeHtml2(error.stack || error.message)}</pre>
696
+ </div>
697
+ ` : "";
698
+ const content = `
699
+ <div class="error-container">
700
+ <h1>${statusCode}</h1>
701
+ <h2>${escapeHtml2(errorMessages[statusCode] || "Error")}</h2>
702
+ <p>${escapeHtml2(message)}</p>
703
+ ${errorDetails}
704
+ <a href="/" class="home-link">Back to Home</a>
705
+ </div>
706
+ `;
707
+ const styles = [`<style>
708
+ body {
709
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
710
+ display: flex;
711
+ justify-content: center;
712
+ align-items: center;
713
+ min-height: 100vh;
714
+ margin: 0;
715
+ background-color: #f7f7f7;
716
+ color: #333;
275
717
  }
276
- getAuth() {
277
- return this.auth;
718
+ .error-container {
719
+ text-align: center;
720
+ padding: 2rem;
721
+ border-radius: 8px;
722
+ background-color: white;
723
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
724
+ max-width: 600px;
725
+ width: 100%;
278
726
  }
279
- start() {
280
- return new Promise((resolve) => {
281
- this.server = this.app.listen(this.config.port, () => {
282
- console.log(`Server running at http://localhost:${this.config.port}`);
283
- resolve();
284
- });
285
- });
727
+ h1 {
728
+ font-size: 6rem;
729
+ margin: 0;
730
+ color: #e74c3c;
286
731
  }
287
- stop() {
288
- return new Promise((resolve, reject) => {
289
- if (!this.server) {
290
- return resolve();
291
- }
292
- this.server.close((err) => {
293
- if (err)
294
- return reject(err);
295
- this.server = null;
296
- resolve();
297
- });
298
- });
732
+ h2 {
733
+ margin-top: 0;
734
+ margin-bottom: 1rem;
735
+ color: #333;
736
+ }
737
+ .error-details {
738
+ text-align: left;
739
+ margin: 2rem 0;
740
+ padding: 1rem;
741
+ background-color: #f8f8f8;
742
+ border-radius: 4px;
743
+ overflow: auto;
744
+ }
745
+ pre {
746
+ margin: 0;
747
+ font-family: monospace;
748
+ font-size: 0.85rem;
749
+ white-space: pre-wrap;
750
+ }
751
+ .home-link {
752
+ display: inline-block;
753
+ margin-top: 1.5rem;
754
+ padding: 0.75rem 1.5rem;
755
+ background-color: #3498db;
756
+ color: white;
757
+ text-decoration: none;
758
+ border-radius: 4px;
759
+ transition: background-color 0.2s;
299
760
  }
300
- restart() {
301
- return this.stop().then(() => this.start());
761
+ .home-link:hover {
762
+ background-color: #2980b9;
302
763
  }
303
- enableSSR(options = {}) {
304
- if (!fs.existsSync(path.resolve(process.cwd(), this.config.pagesDir))) {
305
- fs.mkdirSync(path.resolve(process.cwd(), this.config.pagesDir), { recursive: true });
764
+ </style>`];
765
+ return generateDocument(content, {
766
+ title,
767
+ description: `Error ${statusCode}: ${errorMessages[statusCode] || "Error"}`,
768
+ bodyClasses: "error-page",
769
+ styles
770
+ });
771
+ }
772
+ function generateLoadingPage(message = "Loading...") {
773
+ const content = `
774
+ <div class="loading-container">
775
+ <div class="spinner"></div>
776
+ <p>${escapeHtml2(message)}</p>
777
+ </div>
778
+ `;
779
+ const styles = [`<style>
780
+ body {
781
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
782
+ display: flex;
783
+ justify-content: center;
784
+ align-items: center;
785
+ min-height: 100vh;
786
+ margin: 0;
787
+ background-color: #f7f7f7;
788
+ color: #333;
789
+ }
790
+ .loading-container {
791
+ text-align: center;
792
+ }
793
+ .spinner {
794
+ width: 50px;
795
+ height: 50px;
796
+ border: 5px solid rgba(0, 0, 0, 0.1);
797
+ border-radius: 50%;
798
+ border-top-color: #3498db;
799
+ animation: spin 1s ease-in-out infinite;
800
+ margin: 0 auto 20px;
801
+ }
802
+ @keyframes spin {
803
+ to { transform: rotate(360deg); }
804
+ }
805
+ </style>`];
806
+ return generateDocument(content, {
807
+ title: "Loading",
808
+ bodyClasses: "loading-page",
809
+ styles
810
+ });
811
+ }
812
+ function escapeHtml2(text) {
813
+ return String(text).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
814
+ }
815
+
816
+ // src/server/wasm.ts
817
+ import fs3 from "fs/promises";
818
+ import path3 from "path";
819
+ import { pathToFileURL as pathToFileURL2 } from "url";
820
+ var initialized = false;
821
+ async function initNodeWasm() {
822
+ if (initialized)
823
+ return;
824
+ try {
825
+ globalThis.TextEncoder = globalThis.TextEncoder || TextEncoder;
826
+ globalThis.TextDecoder = globalThis.TextDecoder || TextDecoder;
827
+ initialized = true;
828
+ } catch (error) {
829
+ console.error("[WASM] Failed to initialize Node.js WASM environment:", error);
830
+ throw error;
831
+ }
832
+ }
833
+ async function loadGoRuntime(goWasmPath) {
834
+ if (typeof global.Go === "function") {
835
+ return global.Go;
836
+ }
837
+ const possiblePaths = [
838
+ goWasmPath,
839
+ "./wasm_exec.cjs",
840
+ "./wasm_exec.js",
841
+ "./public/wasm_exec.cjs",
842
+ "./public/wasm_exec.js",
843
+ "./src/wasm_exec.cjs",
844
+ "./src/wasm_exec.js",
845
+ path3.join(process.cwd(), "wasm_exec.cjs"),
846
+ path3.join(process.cwd(), "wasm_exec.js"),
847
+ path3.join(process.cwd(), "public", "wasm_exec.cjs"),
848
+ path3.join(process.cwd(), "public", "wasm_exec.js"),
849
+ path3.join(process.cwd(), "src", "wasm_exec.cjs"),
850
+ path3.join(process.cwd(), "src", "wasm_exec.js")
851
+ ].filter(Boolean);
852
+ for (const wasmExecPath of possiblePaths) {
853
+ try {
854
+ const resolvedPath = path3.resolve(wasmExecPath);
855
+ try {
856
+ const stats = await fs3.stat(resolvedPath);
857
+ if (!stats.isFile()) {
858
+ continue;
306
859
  }
307
- // Set up client-side JS for hydration if needed
308
- if (options.hydratable) {
309
- const staticDir = path.resolve(process.cwd(), this.config.staticDir);
310
- if (!fs.existsSync(staticDir)) {
311
- fs.mkdirSync(staticDir, { recursive: true });
312
- }
313
- // Generate or copy client hydration script
314
- // This is a simplified example - would need more implementation
315
- const clientJsPath = path.join(staticDir, 'client.js');
316
- if (!fs.existsSync(clientJsPath)) {
317
- const clientJsContent = `
318
- // Auto-generated hydration script
319
- import { hydrate } from 'frontend-hamroun';
320
-
321
- // Find SSR content and hydrate it
322
- document.addEventListener('DOMContentLoaded', () => {
323
- const ssrRoots = document.querySelectorAll('[data-ssr-root]');
324
- ssrRoots.forEach(root => {
325
- const componentPath = root.getAttribute('data-component');
326
- if (componentPath) {
327
- import(componentPath).then(module => {
328
- hydrate(module.default, root);
329
- });
330
- }
331
- });
332
- });
333
- `;
334
- fs.writeFileSync(clientJsPath, clientJsContent);
335
- }
860
+ } catch (statError) {
861
+ continue;
862
+ }
863
+ const wasmExecUrl = pathToFileURL2(resolvedPath).href;
864
+ if (global.Go) {
865
+ delete global.Go;
866
+ }
867
+ await import(wasmExecUrl);
868
+ if (typeof global.Go === "function") {
869
+ console.log(`[WASM] Loaded Go runtime from: ${resolvedPath}`);
870
+ return global.Go;
871
+ } else {
872
+ console.warn(`[WASM] File found but Go class not exported: ${resolvedPath}`);
873
+ }
874
+ } catch (error) {
875
+ console.warn(`[WASM] Error loading from ${wasmExecPath}:`, error.message);
876
+ continue;
877
+ }
878
+ }
879
+ throw new Error(
880
+ `[WASM] Could not find wasm_exec.js. Please ensure you have the Go WASM runtime file available.
881
+ You can get it from: https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.js
882
+ Searched paths: ${possiblePaths.join(", ")}`
883
+ );
884
+ }
885
+ async function loadGoWasmFromFile(wasmFilePath, options = {}) {
886
+ try {
887
+ await initNodeWasm();
888
+ if (options.debug) {
889
+ console.log("[WASM] Loading WASM from file:", wasmFilePath);
890
+ }
891
+ const GoRuntime = await loadGoRuntime(options.goWasmPath);
892
+ const go = new GoRuntime();
893
+ const wasmBuffer = await fs3.readFile(wasmFilePath);
894
+ const wasmModule = await WebAssembly.compile(wasmBuffer);
895
+ const baseImports = go.importObject;
896
+ const finalImportObject = {
897
+ ...baseImports,
898
+ ...options.importObject
899
+ };
900
+ if (options.debug) {
901
+ console.log("[WASM] Available import modules:", Object.keys(finalImportObject));
902
+ for (const [moduleName, moduleExports] of Object.entries(finalImportObject)) {
903
+ if (typeof moduleExports === "object" && moduleExports !== null) {
904
+ const exportNames = Object.keys(moduleExports);
905
+ console.log(`[WASM] ${moduleName} import functions:`, exportNames);
336
906
  }
907
+ }
337
908
  }
338
- registerPlugin(plugin) {
339
- plugin(this);
340
- }
341
- addShutdownHandler() {
342
- // Handle graceful shutdown
343
- const gracefulShutdown = async () => {
344
- console.log('Received shutdown signal, closing server...');
345
- try {
346
- await this.stop();
347
- if (this.db) {
348
- await this.db.disconnect();
349
- }
350
- console.log('Server shut down gracefully');
351
- process.exit(0);
352
- }
353
- catch (error) {
354
- console.error('Error during shutdown:', error);
355
- process.exit(1);
356
- }
357
- };
358
- // Listen for termination signals
359
- process.on('SIGTERM', gracefulShutdown);
360
- process.on('SIGINT', gracefulShutdown);
361
- }
362
- }
363
- // Add server utility functions
364
- /**
365
- * Creates and configures a new server instance
366
- */
367
- export function createServer(config = {}) {
368
- return new Server(config);
369
- }
370
- /**
371
- * Creates a new server with sensible defaults for development
372
- */
373
- export function createDevServer(options = {}) {
374
- return new Server({
375
- port: options.port || 3000,
376
- enableCors: options.enableCors !== false,
377
- apiDir: './src/api',
378
- pagesDir: './src/pages',
379
- staticDir: './public'
909
+ const instance = await WebAssembly.instantiate(wasmModule, finalImportObject);
910
+ if (options.debug) {
911
+ console.log("[WASM] WASM instance created, starting Go runtime...");
912
+ }
913
+ const runPromise = go.run(instance);
914
+ if (options.debug) {
915
+ console.log("[WASM] Go runtime started, waiting for initialization...");
916
+ }
917
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
918
+ if (options.debug) {
919
+ console.log("[WASM] Scanning global object for Go functions...");
920
+ }
921
+ const functions = {};
922
+ const globalObj = global;
923
+ const allKeys = Object.getOwnPropertyNames(globalObj);
924
+ const goKeys = allKeys.filter((key) => key.startsWith("go"));
925
+ if (options.debug) {
926
+ console.log('[WASM] All keys starting with "go":', goKeys);
927
+ }
928
+ for (const key of goKeys) {
929
+ if (typeof globalObj[key] === "function") {
930
+ functions[key] = globalObj[key];
931
+ if (options.debug) {
932
+ console.log(`[WASM] Found function: ${key}`);
933
+ }
934
+ }
935
+ }
936
+ const wasmInstance = {
937
+ instance,
938
+ module: wasmModule,
939
+ exports: instance.exports,
940
+ functions
941
+ };
942
+ if (options.onLoad) {
943
+ options.onLoad(wasmInstance);
944
+ }
945
+ if (options.debug) {
946
+ console.log(`[WASM] Module loaded successfully with ${Object.keys(functions).length} functions`);
947
+ console.log("[WASM] Final function list:", Object.keys(functions));
948
+ }
949
+ runPromise.catch((error) => {
950
+ if (options.debug) {
951
+ console.log("[WASM] Go program finished with error:", error);
952
+ }
380
953
  });
954
+ return wasmInstance;
955
+ } catch (error) {
956
+ console.error("[WASM] Failed to load Go WASM module:", error);
957
+ throw error;
958
+ }
381
959
  }
382
- /**
383
- * Creates a production-ready server with optimized settings
384
- */
385
- export function createProductionServer(config) {
386
- const server = new Server({
387
- ...config,
388
- // Fix the PORT parsing
389
- port: config.port || (process.env.PORT ? parseInt(process.env.PORT) : 8080)
960
+
961
+ // src/server/index.ts
962
+ function getComponentName(filePath, pagesDir) {
963
+ try {
964
+ const relativePath = path4.relative(pagesDir, filePath);
965
+ const withoutExt = relativePath.replace(/\.[^/.]+$/, "");
966
+ const cleanPath = withoutExt.replace(/\/index$/, "");
967
+ return `/pages/${cleanPath}`;
968
+ } catch (e) {
969
+ return "";
970
+ }
971
+ }
972
+ var Server = class {
973
+ constructor(config = {}) {
974
+ this.server = null;
975
+ this.db = null;
976
+ this.auth = null;
977
+ this.config = {
978
+ port: 3e3,
979
+ apiDir: "./api",
980
+ pagesDir: "./pages",
981
+ staticDir: "./public",
982
+ enableCors: true,
983
+ ...config
984
+ };
985
+ this.initializeApp();
986
+ }
987
+ async initializeApp() {
988
+ try {
989
+ const express = await import("express");
990
+ this.app = express.default();
991
+ this.app.use(express.default.json());
992
+ this.app.use(express.default.urlencoded({ extended: true }));
993
+ if (this.config.enableCors) {
994
+ try {
995
+ const cors = await import("cors");
996
+ this.app.use(cors.default(this.config.corsOptions));
997
+ } catch (error) {
998
+ console.warn("CORS not available - install with: npm install cors @types/cors");
999
+ }
1000
+ }
1001
+ if (this.config.db) {
1002
+ this.db = new Database(this.config.db);
1003
+ }
1004
+ if (this.config.auth) {
1005
+ this.auth = new AuthService({
1006
+ secret: this.config.auth.secret,
1007
+ expiresIn: this.config.auth.expiresIn
1008
+ });
1009
+ this.app.use(this.auth.initialize());
1010
+ }
1011
+ const staticPath = path4.resolve(process.cwd(), this.config.staticDir);
1012
+ if (fs4.existsSync(staticPath)) {
1013
+ this.app.use(express.default.static(staticPath));
1014
+ }
1015
+ this.setupApiRoutes();
1016
+ this.setupPageRoutes();
1017
+ this.app.use(notFoundHandler);
1018
+ this.app.use(errorHandler);
1019
+ } catch (error) {
1020
+ if (error.code === "MODULE_NOT_FOUND" && error.message.includes("express")) {
1021
+ throw new Error("Express not installed. Run: npm install express @types/express");
1022
+ }
1023
+ throw error;
1024
+ }
1025
+ }
1026
+ setupApiRoutes() {
1027
+ const apiRouter = new ApiRouter(this.config.apiDir, this.auth);
1028
+ this.app.use("/api", requestLogger, apiRouter.router);
1029
+ this.app.get("/api-docs", (req, res) => {
1030
+ const apiDocs = this.generateApiDocs();
1031
+ res.json(apiDocs);
390
1032
  });
391
- // Add shutdown handler for production environments
392
- server.addShutdownHandler();
393
- return server;
394
- }
395
- // Export additional utility functions
396
- export function getRequestIp(req) {
397
- return req.ip ||
398
- req.headers['x-forwarded-for']?.split(',')[0].trim() ||
399
- req.socket.remoteAddress ||
400
- '';
401
- }
402
- export function parseCookies(req) {
403
- const cookies = {};
404
- const cookieHeader = req.headers.cookie;
405
- if (cookieHeader) {
406
- cookieHeader.split(';').forEach(cookie => {
407
- const [name, value] = cookie.split('=').map(c => c.trim());
408
- cookies[name] = value;
1033
+ }
1034
+ generateApiDocs() {
1035
+ const apiDir = path4.resolve(process.cwd(), this.config.apiDir);
1036
+ const docs = { endpoints: [] };
1037
+ if (fs4.existsSync(apiDir)) {
1038
+ this.scanApiDirectory(apiDir, "", docs.endpoints);
1039
+ }
1040
+ return docs;
1041
+ }
1042
+ scanApiDirectory(dirPath, routePrefix, endpoints) {
1043
+ const entries = fs4.readdirSync(dirPath, { withFileTypes: true });
1044
+ for (const entry of entries) {
1045
+ const fullPath = path4.join(dirPath, entry.name);
1046
+ if (entry.isDirectory()) {
1047
+ this.scanApiDirectory(fullPath, `${routePrefix}/${entry.name}`, endpoints);
1048
+ } else if (entry.name.endsWith(".js") || entry.name.endsWith(".ts")) {
1049
+ const routePath = this.getRoutePath(entry.name, routePrefix);
1050
+ endpoints.push({
1051
+ path: routePath,
1052
+ file: path4.relative(process.cwd(), fullPath),
1053
+ methods: this.detectApiMethods(fullPath)
409
1054
  });
1055
+ }
410
1056
  }
411
- return cookies;
412
- }
413
- // Fix the renderComponent function
414
- export const renderComponent = async (Component, props = {}) => {
1057
+ }
1058
+ getRoutePath(filename, routePrefix) {
1059
+ const baseName = path4.basename(filename, path4.extname(filename));
1060
+ if (baseName === "index") {
1061
+ return routePrefix || "/";
1062
+ }
1063
+ const paramMatch = baseName.match(/^\[(.+)\]$/);
1064
+ if (paramMatch) {
1065
+ return `${routePrefix}/:${paramMatch[1]}`;
1066
+ }
1067
+ return `${routePrefix}/${baseName}`;
1068
+ }
1069
+ detectApiMethods(filePath) {
1070
+ try {
1071
+ const content = fs4.readFileSync(filePath, "utf8");
1072
+ const methods = ["get", "post", "put", "delete", "patch", "options"];
1073
+ return methods.filter(
1074
+ (method) => new RegExp(`export (async )?function ${method}\\s*\\(`).test(content) || new RegExp(`export const ${method}\\s*=`).test(content)
1075
+ );
1076
+ } catch (error) {
1077
+ return [];
1078
+ }
1079
+ }
1080
+ setupPageRoutes() {
1081
+ const pagesPath = path4.resolve(process.cwd(), this.config.pagesDir);
1082
+ if (!fs4.existsSync(pagesPath)) {
1083
+ return;
1084
+ }
1085
+ this.app.get("*", async (req, res) => {
1086
+ try {
1087
+ const { html, statusCode } = await this.renderPage(req.path);
1088
+ res.status(statusCode || 200).send(html);
1089
+ } catch (error) {
1090
+ console.error("Error rendering page:", error);
1091
+ const errorHtml = generateErrorPage(500, "Server Error");
1092
+ res.status(500).send(errorHtml);
1093
+ }
1094
+ });
1095
+ }
1096
+ async renderPage(routePath) {
415
1097
  try {
416
- // Create HTML string from component
417
- const html = renderToString(Component(props));
1098
+ const pagesDir = this.config.pagesDir ? path4.resolve(process.cwd(), this.config.pagesDir) : "";
1099
+ if (!pagesDir || !fs4.existsSync(pagesDir)) {
418
1100
  return {
419
- html,
420
- success: true
1101
+ html: generateErrorPage(404, "Pages directory not found"),
1102
+ statusCode: 404
421
1103
  };
422
- }
423
- catch (error) {
424
- console.error('Error rendering component:', error);
1104
+ }
1105
+ let normalizedPath = routePath;
1106
+ if (!normalizedPath.startsWith("/")) {
1107
+ normalizedPath = "/" + normalizedPath;
1108
+ }
1109
+ if (normalizedPath === "/") {
1110
+ normalizedPath = "/index";
1111
+ }
1112
+ let pagePath = "";
1113
+ const possiblePaths = [
1114
+ path4.join(pagesDir, `${normalizedPath}.js`),
1115
+ path4.join(pagesDir, `${normalizedPath}.ts`),
1116
+ path4.join(pagesDir, `${normalizedPath}.jsx`),
1117
+ path4.join(pagesDir, `${normalizedPath}.tsx`),
1118
+ path4.join(pagesDir, `${normalizedPath}/index.js`),
1119
+ path4.join(pagesDir, `${normalizedPath}/index.ts`),
1120
+ path4.join(pagesDir, `${normalizedPath}/index.jsx`),
1121
+ path4.join(pagesDir, `${normalizedPath}/index.tsx`)
1122
+ ];
1123
+ for (const p of possiblePaths) {
1124
+ if (fs4.existsSync(p)) {
1125
+ pagePath = p;
1126
+ break;
1127
+ }
1128
+ }
1129
+ if (!pagePath) {
425
1130
  return {
426
- html: `<div class="error">Error rendering component</div>`,
427
- success: false,
428
- error
1131
+ html: generateErrorPage(404, `Page not found: ${normalizedPath}`),
1132
+ statusCode: 404
429
1133
  };
1134
+ }
1135
+ try {
1136
+ const pageModule = await import(pagePath);
1137
+ if (!pageModule || !pageModule.default) {
1138
+ throw new Error(`No default export found in ${pagePath}`);
1139
+ }
1140
+ const PageComponent = pageModule.default;
1141
+ const initialProps = {
1142
+ // Provide any initial props here
1143
+ path: normalizedPath,
1144
+ query: {},
1145
+ // Could be parsed from URL
1146
+ api: { serverTime: (/* @__PURE__ */ new Date()).toISOString() }
1147
+ };
1148
+ const { html, success, error } = await renderComponent(PageComponent, initialProps);
1149
+ if (!success) {
1150
+ return {
1151
+ html: generateErrorPage(500, "Failed to render page", error instanceof Error ? error : new Error("Unknown error")),
1152
+ statusCode: 500
1153
+ };
1154
+ }
1155
+ let pageTitle = "My App";
1156
+ try {
1157
+ if (typeof PageComponent.getTitle === "function") {
1158
+ pageTitle = PageComponent.getTitle(initialProps);
1159
+ } else if (PageComponent.title) {
1160
+ pageTitle = PageComponent.title;
1161
+ }
1162
+ } catch (e) {
1163
+ }
1164
+ const componentName = getComponentName(pagePath, pagesDir);
1165
+ const fullHtml = generateDocument(html, {
1166
+ title: pageTitle,
1167
+ // Get description from component if available
1168
+ description: typeof PageComponent.getDescription === "function" ? PageComponent.getDescription(initialProps) : PageComponent.description || "",
1169
+ // Add scripts for client-side hydration
1170
+ scripts: ["/client.js"],
1171
+ // Add any custom meta tags
1172
+ meta: typeof PageComponent.getMeta === "function" ? PageComponent.getMeta(initialProps) : {},
1173
+ // Add custom styles
1174
+ styles: ["/styles.css"],
1175
+ // Add initial data for client-side hydration
1176
+ initialData: initialProps,
1177
+ // Add component information for hydration
1178
+ componentName
1179
+ });
1180
+ return { html: fullHtml, statusCode: 200 };
1181
+ } catch (error) {
1182
+ console.error("Error rendering page:", error);
1183
+ return {
1184
+ html: generateErrorPage(500, "Error rendering page", error instanceof Error ? error : new Error("Unknown error")),
1185
+ statusCode: 500
1186
+ };
1187
+ }
1188
+ } catch (error) {
1189
+ console.error("Error finding page:", error);
1190
+ return {
1191
+ html: generateErrorPage(500, "Server error", error instanceof Error ? error : new Error("Unknown error")),
1192
+ statusCode: 500
1193
+ };
1194
+ }
1195
+ }
1196
+ getExpressApp() {
1197
+ return this.app;
1198
+ }
1199
+ getDatabase() {
1200
+ return this.db;
1201
+ }
1202
+ getAuth() {
1203
+ return this.auth;
1204
+ }
1205
+ start() {
1206
+ return new Promise((resolve) => {
1207
+ this.server = this.app.listen(this.config.port, () => {
1208
+ console.log(`Server running at http://localhost:${this.config.port}`);
1209
+ resolve();
1210
+ });
1211
+ });
1212
+ }
1213
+ stop() {
1214
+ return new Promise((resolve, reject) => {
1215
+ if (!this.server) {
1216
+ return resolve();
1217
+ }
1218
+ this.server.close((err) => {
1219
+ if (err)
1220
+ return reject(err);
1221
+ this.server = null;
1222
+ resolve();
1223
+ });
1224
+ });
1225
+ }
1226
+ restart() {
1227
+ return this.stop().then(() => this.start());
1228
+ }
1229
+ enableSSR(options = {}) {
1230
+ if (!fs4.existsSync(path4.resolve(process.cwd(), this.config.pagesDir))) {
1231
+ fs4.mkdirSync(path4.resolve(process.cwd(), this.config.pagesDir), { recursive: true });
1232
+ }
1233
+ if (options.hydratable) {
1234
+ const staticDir = path4.resolve(process.cwd(), this.config.staticDir);
1235
+ if (!fs4.existsSync(staticDir)) {
1236
+ fs4.mkdirSync(staticDir, { recursive: true });
1237
+ }
1238
+ const clientJsPath = path4.join(staticDir, "client.js");
1239
+ if (!fs4.existsSync(clientJsPath)) {
1240
+ const clientJsContent = `
1241
+ // Auto-generated hydration script
1242
+ import { hydrate } from 'frontend-hamroun';
1243
+
1244
+ // Find SSR content and hydrate it
1245
+ document.addEventListener('DOMContentLoaded', () => {
1246
+ const ssrRoots = document.querySelectorAll('[data-ssr-root]');
1247
+ ssrRoots.forEach(root => {
1248
+ const componentPath = root.getAttribute('data-component');
1249
+ if (componentPath) {
1250
+ import(componentPath).then(module => {
1251
+ hydrate(module.default, root);
1252
+ });
1253
+ }
1254
+ });
1255
+ });
1256
+ `;
1257
+ fs4.writeFileSync(clientJsPath, clientJsContent);
1258
+ }
430
1259
  }
1260
+ }
1261
+ registerPlugin(plugin) {
1262
+ plugin(this);
1263
+ }
1264
+ addShutdownHandler() {
1265
+ const gracefulShutdown = async () => {
1266
+ console.log("Received shutdown signal, closing server...");
1267
+ try {
1268
+ await this.stop();
1269
+ if (this.db) {
1270
+ await this.db.disconnect();
1271
+ }
1272
+ console.log("Server shut down gracefully");
1273
+ process.exit(0);
1274
+ } catch (error) {
1275
+ console.error("Error during shutdown:", error);
1276
+ process.exit(1);
1277
+ }
1278
+ };
1279
+ process.on("SIGTERM", gracefulShutdown);
1280
+ process.on("SIGINT", gracefulShutdown);
1281
+ }
1282
+ };
1283
+ function createServer(config = {}) {
1284
+ return new Server(config);
1285
+ }
1286
+ function createDevServer(options = {}) {
1287
+ return new Server({
1288
+ port: options.port || 3e3,
1289
+ enableCors: options.enableCors !== false,
1290
+ apiDir: "./src/api",
1291
+ pagesDir: "./src/pages",
1292
+ staticDir: "./public"
1293
+ });
1294
+ }
1295
+ function createProductionServer(config) {
1296
+ const server = new Server({
1297
+ ...config,
1298
+ // Fix the PORT parsing
1299
+ port: config.port || (process.env.PORT ? parseInt(process.env.PORT) : 8080)
1300
+ });
1301
+ server.addShutdownHandler();
1302
+ return server;
1303
+ }
1304
+ function getRequestIp(req) {
1305
+ return req.ip || req.headers["x-forwarded-for"]?.split(",")[0].trim() || req.socket.remoteAddress || "";
1306
+ }
1307
+ function parseCookies(req) {
1308
+ const cookies = {};
1309
+ const cookieHeader = req.headers.cookie;
1310
+ if (cookieHeader) {
1311
+ cookieHeader.split(";").forEach((cookie) => {
1312
+ const [name, value] = cookie.split("=").map((c) => c.trim());
1313
+ cookies[name] = value;
1314
+ });
1315
+ }
1316
+ return cookies;
1317
+ }
1318
+ var renderComponent = async (Component, props = {}) => {
1319
+ try {
1320
+ const html = renderToString(Component(props));
1321
+ return {
1322
+ html,
1323
+ success: true
1324
+ };
1325
+ } catch (error) {
1326
+ console.error("Error rendering component:", error);
1327
+ return {
1328
+ html: `<div class="error">Error rendering component</div>`,
1329
+ success: false,
1330
+ error
1331
+ };
1332
+ }
1333
+ };
1334
+ var server_default = {
1335
+ Server,
1336
+ createServer,
1337
+ createDevServer,
1338
+ createProductionServer,
1339
+ rateLimit,
1340
+ requestLogger,
1341
+ errorHandler,
1342
+ notFoundHandler,
1343
+ loadGoWasmFromFile,
1344
+ // Template utilities
1345
+ templates: templates_exports,
1346
+ // Server utilities
1347
+ utils: utils_exports,
1348
+ // Additional exports
1349
+ renderComponent,
1350
+ renderToString,
1351
+ getRequestIp,
1352
+ parseCookies,
1353
+ Database,
1354
+ AuthService,
1355
+ ApiRouter
431
1356
  };
432
- // Export WASM utilities for server-side usage
433
- export { initNodeWasm, loadGoWasmFromFile } from './wasm.js';
434
- // Import and export server utilities
435
- export * from './utils.js';
436
- export * from './templates.js';
437
- // Export the Database and AuthService classes
438
- export { Database } from './database.js';
439
- export { AuthService } from './auth.js';
440
- export { ApiRouter } from './api-router.js';
441
- export { requestLogger, errorHandler, notFoundHandler, rateLimit } from './middleware.js';
442
- // Export renderToString for convenient SSR
443
- export { renderToString };
444
- // Ensure the default export includes all required functions
445
- export default {
446
- Server,
447
- createServer,
448
- createDevServer,
449
- createProductionServer,
450
- rateLimit,
451
- requestLogger,
452
- errorHandler,
453
- notFoundHandler,
454
- loadGoWasmFromFile,
455
- // Template utilities
456
- templates,
457
- // Server utilities
458
- utils,
459
- // Additional exports
460
- renderComponent,
461
- renderToString,
462
- getRequestIp,
463
- parseCookies,
464
- Database,
465
- AuthService,
466
- ApiRouter
1357
+ export {
1358
+ ApiRouter,
1359
+ AuthService,
1360
+ Database,
1361
+ Server,
1362
+ createDevServer,
1363
+ createProductionServer,
1364
+ createServer,
1365
+ server_default as default,
1366
+ ensureDirectory,
1367
+ errorHandler,
1368
+ generateDocument,
1369
+ generateErrorPage,
1370
+ generateLoadingPage,
1371
+ generateToken,
1372
+ getEnvironmentInfo,
1373
+ getPagination,
1374
+ getRequestIp,
1375
+ hashString,
1376
+ initNodeWasm,
1377
+ isDirectoryEmpty,
1378
+ loadGoWasmFromFile,
1379
+ notFoundHandler,
1380
+ parseCookies,
1381
+ rateLimit,
1382
+ readJsonFile,
1383
+ renderComponent,
1384
+ renderToString,
1385
+ requestLogger,
1386
+ safeJsonParse,
1387
+ sendError,
1388
+ sendSuccess,
1389
+ validateFields,
1390
+ validateFileUpload,
1391
+ writeJsonFile
467
1392
  };
468
- //# sourceMappingURL=index.js.map
1393
+ //# sourceMappingURL=index.js.map