frontend-hamroun 1.2.83 → 1.2.85

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 (245) hide show
  1. package/bin/cli.js +57 -869
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.client.cjs +1 -1
  5. package/dist/index.client.cjs.map +1 -1
  6. package/dist/index.client.js +2 -2
  7. package/dist/index.client.js.map +1 -1
  8. package/dist/index.js +116 -136
  9. package/dist/index.js.map +1 -1
  10. package/dist/jsx-runtime.cjs.map +1 -1
  11. package/dist/jsx-runtime.js.map +1 -1
  12. package/dist/renderer-DaVfBeVi.cjs +2 -0
  13. package/dist/renderer-DaVfBeVi.cjs.map +1 -0
  14. package/dist/renderer-nfT7XSpo.js +61 -0
  15. package/dist/renderer-nfT7XSpo.js.map +1 -0
  16. package/dist/server-renderer-B5b0Q0ck.cjs +2 -0
  17. package/dist/server-renderer-B5b0Q0ck.cjs.map +1 -0
  18. package/dist/{server-renderer-C1WXH-zV.js → server-renderer-C4MB-jAp.js} +6 -39
  19. package/dist/server-renderer-C4MB-jAp.js.map +1 -0
  20. package/dist/server-renderer.cjs +1 -1
  21. package/dist/server-renderer.js +1 -1
  22. package/package.json +1 -1
  23. package/templates/basic-app/build.d.ts +2 -0
  24. package/templates/basic-app/build.d.ts.map +1 -0
  25. package/templates/basic-app/dev.d.ts +2 -0
  26. package/templates/basic-app/dev.d.ts.map +1 -0
  27. package/templates/basic-app/esbuild.config.d.ts +2 -0
  28. package/templates/basic-app/esbuild.config.d.ts.map +1 -0
  29. package/templates/basic-app/postcss.config.d.ts +8 -0
  30. package/templates/basic-app/postcss.config.d.ts.map +1 -0
  31. package/templates/basic-app/server.d.ts +2 -0
  32. package/templates/basic-app/server.d.ts.map +1 -0
  33. package/templates/basic-app/src/App.d.ts +2 -0
  34. package/templates/basic-app/src/App.d.ts.map +1 -0
  35. package/templates/basic-app/src/App.js +148 -0
  36. package/templates/basic-app/src/App.tsx +397 -19
  37. package/templates/basic-app/src/client.d.ts +2 -0
  38. package/templates/basic-app/src/client.d.ts.map +1 -0
  39. package/templates/basic-app/src/client.js +6 -0
  40. package/templates/basic-app/src/components/Counter.d.ts +4 -0
  41. package/templates/basic-app/src/components/Counter.d.ts.map +1 -0
  42. package/templates/basic-app/src/components/Counter.js +9 -0
  43. package/templates/basic-app/src/jsx-shim.d.ts +8 -0
  44. package/templates/basic-app/src/jsx-shim.d.ts.map +1 -0
  45. package/templates/basic-app/src/main.d.ts +2 -0
  46. package/templates/basic-app/src/main.d.ts.map +1 -0
  47. package/templates/basic-app/src/main.js +57 -0
  48. package/templates/basic-app/src/server.d.ts +2 -0
  49. package/templates/basic-app/src/server.d.ts.map +1 -0
  50. package/templates/basic-app/tailwind.config.d.ts +9 -0
  51. package/templates/basic-app/tailwind.config.d.ts.map +1 -0
  52. package/templates/basic-app/vite.config.d.ts +3 -0
  53. package/templates/basic-app/vite.config.d.ts.map +1 -0
  54. package/templates/basic-app/vite.config.js +7 -0
  55. package/templates/complete-app/api/hello.d.ts +1 -0
  56. package/templates/complete-app/api/hello.d.ts.map +1 -0
  57. package/templates/complete-app/client.d.ts +2 -0
  58. package/templates/complete-app/client.d.ts.map +1 -0
  59. package/templates/complete-app/lib/frontend-hamroun.d.ts +18 -0
  60. package/templates/complete-app/lib/frontend-hamroun.d.ts.map +1 -0
  61. package/templates/complete-app/pages/about.d.ts +7 -0
  62. package/templates/complete-app/pages/about.d.ts.map +1 -0
  63. package/templates/complete-app/pages/index.d.ts +7 -0
  64. package/templates/complete-app/pages/index.d.ts.map +1 -0
  65. package/templates/complete-app/pages/wasm-demo.d.ts +7 -0
  66. package/templates/complete-app/pages/wasm-demo.d.ts.map +1 -0
  67. package/templates/complete-app/public/client.d.ts +17 -0
  68. package/templates/complete-app/public/client.d.ts.map +1 -0
  69. package/templates/complete-app/server.d.ts +2 -0
  70. package/templates/complete-app/server.d.ts.map +1 -0
  71. package/templates/complete-app/server.js +236 -218
  72. package/templates/complete-app/src/App.d.ts +2 -0
  73. package/templates/complete-app/src/App.d.ts.map +1 -0
  74. package/templates/complete-app/src/App.js +27 -0
  75. package/templates/complete-app/src/client.d.ts +2 -0
  76. package/templates/complete-app/src/client.d.ts.map +1 -0
  77. package/templates/complete-app/src/client.js +52 -0
  78. package/templates/complete-app/src/pages/index.d.ts +2 -0
  79. package/templates/complete-app/src/pages/index.d.ts.map +1 -0
  80. package/templates/complete-app/src/pages/index.js +19 -0
  81. package/templates/complete-app/src/server.d.ts +2 -0
  82. package/templates/complete-app/src/server.d.ts.map +1 -0
  83. package/templates/complete-app/src/server.js +192 -0
  84. package/templates/complete-app/vite.config.d.ts +3 -0
  85. package/templates/complete-app/vite.config.d.ts.map +1 -0
  86. package/templates/complete-app/vite.config.js +29 -57
  87. package/templates/fullstack-app/api/hello.d.ts +4 -0
  88. package/templates/fullstack-app/api/hello.d.ts.map +1 -0
  89. package/templates/fullstack-app/api/hello.js +14 -11
  90. package/templates/fullstack-app/api/users/[id].d.ts +5 -0
  91. package/templates/fullstack-app/api/users/[id].d.ts.map +1 -0
  92. package/templates/fullstack-app/api/users/[id].js +52 -0
  93. package/templates/fullstack-app/api/users/index.d.ts +4 -0
  94. package/templates/fullstack-app/api/users/index.d.ts.map +1 -0
  95. package/templates/fullstack-app/api/users/index.js +25 -0
  96. package/templates/fullstack-app/build/main.d.ts +211 -0
  97. package/templates/fullstack-app/build/main.d.ts.map +1 -0
  98. package/templates/fullstack-app/build.d.ts +2 -0
  99. package/templates/fullstack-app/build.d.ts.map +1 -0
  100. package/templates/fullstack-app/build.js +190 -0
  101. package/templates/fullstack-app/postcss.config.d.ts +5 -0
  102. package/templates/fullstack-app/postcss.config.d.ts.map +1 -0
  103. package/templates/fullstack-app/process-tailwind.d.ts +2 -0
  104. package/templates/fullstack-app/process-tailwind.d.ts.map +1 -0
  105. package/templates/fullstack-app/public/route-handler.d.ts +1 -0
  106. package/templates/fullstack-app/public/route-handler.d.ts.map +1 -0
  107. package/templates/fullstack-app/server.d.ts +2 -0
  108. package/templates/fullstack-app/server.d.ts.map +1 -0
  109. package/templates/fullstack-app/server.js +428 -266
  110. package/templates/fullstack-app/src/client.d.ts +2 -0
  111. package/templates/fullstack-app/src/client.d.ts.map +1 -0
  112. package/templates/fullstack-app/src/components/ClientHome.d.ts +1 -0
  113. package/templates/fullstack-app/src/components/ClientHome.d.ts.map +1 -0
  114. package/templates/fullstack-app/src/components/ClientHome.js +1 -0
  115. package/templates/fullstack-app/src/components/ErrorBoundary.d.ts +7 -0
  116. package/templates/fullstack-app/src/components/ErrorBoundary.d.ts.map +1 -0
  117. package/templates/fullstack-app/src/components/ErrorBoundary.js +12 -0
  118. package/templates/fullstack-app/src/components/Layout.d.ts +7 -0
  119. package/templates/fullstack-app/src/components/Layout.d.ts.map +1 -0
  120. package/templates/fullstack-app/src/components/Layout.js +4 -0
  121. package/templates/fullstack-app/src/components/StateDemo.d.ts +2 -0
  122. package/templates/fullstack-app/src/components/StateDemo.d.ts.map +1 -0
  123. package/templates/fullstack-app/src/components/StateDemo.js +86 -0
  124. package/templates/fullstack-app/src/components/UserList.d.ts +11 -0
  125. package/templates/fullstack-app/src/components/UserList.d.ts.map +1 -0
  126. package/templates/fullstack-app/src/components/UserList.js +7 -0
  127. package/templates/fullstack-app/src/config.d.ts +29 -0
  128. package/templates/fullstack-app/src/config.d.ts.map +1 -0
  129. package/templates/fullstack-app/src/config.js +36 -0
  130. package/templates/fullstack-app/src/data/api.d.ts +35 -0
  131. package/templates/fullstack-app/src/data/api.d.ts.map +1 -0
  132. package/templates/fullstack-app/src/data/api.js +173 -0
  133. package/templates/fullstack-app/src/main.d.ts +7 -0
  134. package/templates/fullstack-app/src/main.d.ts.map +1 -0
  135. package/templates/fullstack-app/src/main.js +130 -0
  136. package/templates/fullstack-app/src/middleware.d.ts +10 -0
  137. package/templates/fullstack-app/src/middleware.d.ts.map +1 -0
  138. package/templates/fullstack-app/src/middleware.js +14 -0
  139. package/templates/fullstack-app/src/pages/404.d.ts +4 -0
  140. package/templates/fullstack-app/src/pages/404.d.ts.map +1 -0
  141. package/templates/fullstack-app/src/pages/404.js +4 -0
  142. package/templates/fullstack-app/src/pages/[id].d.ts +1 -0
  143. package/templates/fullstack-app/src/pages/[id].d.ts.map +1 -0
  144. package/templates/fullstack-app/src/pages/[id].js +1 -0
  145. package/templates/fullstack-app/src/pages/_app.d.ts +6 -0
  146. package/templates/fullstack-app/src/pages/_app.d.ts.map +1 -0
  147. package/templates/fullstack-app/src/pages/_app.js +6 -0
  148. package/templates/fullstack-app/src/pages/_document.d.ts +7 -0
  149. package/templates/fullstack-app/src/pages/_document.d.ts.map +1 -0
  150. package/templates/fullstack-app/src/pages/_document.js +4 -0
  151. package/templates/fullstack-app/src/pages/_error.d.ts +4 -0
  152. package/templates/fullstack-app/src/pages/_error.d.ts.map +1 -0
  153. package/templates/fullstack-app/src/pages/_error.js +8 -0
  154. package/templates/fullstack-app/src/pages/about/index.d.ts +5 -0
  155. package/templates/fullstack-app/src/pages/about/index.d.ts.map +1 -0
  156. package/templates/fullstack-app/src/pages/about/index.js +6 -0
  157. package/templates/fullstack-app/src/pages/about.d.ts +10 -0
  158. package/templates/fullstack-app/src/pages/about.d.ts.map +1 -0
  159. package/templates/fullstack-app/src/pages/about.js +21 -0
  160. package/templates/fullstack-app/src/pages/api/users/[id].d.ts +6 -0
  161. package/templates/fullstack-app/src/pages/api/users/[id].d.ts.map +1 -0
  162. package/templates/fullstack-app/src/pages/api/users/[id].js +51 -0
  163. package/templates/fullstack-app/src/pages/api/users/index.d.ts +4 -0
  164. package/templates/fullstack-app/src/pages/api/users/index.d.ts.map +1 -0
  165. package/templates/fullstack-app/src/pages/api/users/index.js +33 -0
  166. package/templates/fullstack-app/src/pages/index.d.ts +21 -0
  167. package/templates/fullstack-app/src/pages/index.d.ts.map +1 -0
  168. package/templates/fullstack-app/src/pages/index.js +66 -0
  169. package/templates/fullstack-app/src/pages/users/[id].d.ts +38 -0
  170. package/templates/fullstack-app/src/pages/users/[id].d.ts.map +1 -0
  171. package/templates/fullstack-app/src/pages/users/[id].js +79 -0
  172. package/templates/fullstack-app/src/pages/users.d.ts +14 -0
  173. package/templates/fullstack-app/src/pages/users.d.ts.map +1 -0
  174. package/templates/fullstack-app/src/pages/users.js +14 -0
  175. package/templates/fullstack-app/src/pages/wasm-demo.d.ts +1 -0
  176. package/templates/fullstack-app/src/pages/wasm-demo.d.ts.map +1 -0
  177. package/templates/fullstack-app/src/pages/wasm-demo.js +2 -0
  178. package/templates/fullstack-app/src/router.d.ts +22 -0
  179. package/templates/fullstack-app/src/router.d.ts.map +1 -0
  180. package/templates/fullstack-app/src/router.js +210 -0
  181. package/templates/fullstack-app/tailwind.config.d.ts +3 -0
  182. package/templates/fullstack-app/tailwind.config.d.ts.map +1 -0
  183. package/templates/fullstack-app/vite.config.d.ts +3 -0
  184. package/templates/fullstack-app/vite.config.d.ts.map +1 -0
  185. package/templates/ssr-template/dist/client/assets/main-D-VH3xOb.d.ts +2 -0
  186. package/templates/ssr-template/dist/client/assets/main-D-VH3xOb.d.ts.map +1 -0
  187. package/templates/ssr-template/dist/client.d.ts +85 -0
  188. package/templates/ssr-template/dist/client.d.ts.map +1 -0
  189. package/templates/ssr-template/dist/server.d.ts +2 -0
  190. package/templates/ssr-template/dist/server.d.ts.map +1 -0
  191. package/templates/ssr-template/esbuild.config.d.ts +2 -0
  192. package/templates/ssr-template/esbuild.config.d.ts.map +1 -0
  193. package/templates/ssr-template/jsx-shim.d.ts +2 -0
  194. package/templates/ssr-template/jsx-shim.d.ts.map +1 -0
  195. package/templates/ssr-template/src/App.d.ts +2 -0
  196. package/templates/ssr-template/src/App.d.ts.map +1 -0
  197. package/templates/ssr-template/src/App.js +625 -0
  198. package/templates/ssr-template/src/App.tsx +43 -18
  199. package/templates/ssr-template/src/client.d.ts +2 -0
  200. package/templates/ssr-template/src/client.d.ts.map +1 -0
  201. package/templates/ssr-template/src/client.js +3 -0
  202. package/templates/ssr-template/src/server.d.ts +2 -0
  203. package/templates/ssr-template/src/server.d.ts.map +1 -0
  204. package/templates/ssr-template/src/server.js +29 -0
  205. package/templates/ssr-template/vite.config.d.ts +3 -0
  206. package/templates/ssr-template/vite.config.d.ts.map +1 -0
  207. package/templates/ssr-template/vite.config.js +30 -0
  208. package/templates/wasm/build-wasm.d.ts +2 -0
  209. package/templates/wasm/build-wasm.d.ts.map +1 -0
  210. package/templates/wasm/dist/assets/index-BNqTDBdE.d.ts +30 -0
  211. package/templates/wasm/dist/assets/index-BNqTDBdE.d.ts.map +1 -0
  212. package/templates/wasm/dist/wasm_exec.d.ts +1 -0
  213. package/templates/wasm/dist/wasm_exec.d.ts.map +1 -0
  214. package/templates/wasm/esbuild.config.d.ts +2 -0
  215. package/templates/wasm/esbuild.config.d.ts.map +1 -0
  216. package/templates/wasm/go/wasm_exec.d.ts +1 -0
  217. package/templates/wasm/go/wasm_exec.d.ts.map +1 -0
  218. package/templates/wasm/jsx-shim.d.ts +5 -0
  219. package/templates/wasm/jsx-shim.d.ts.map +1 -0
  220. package/templates/wasm/public/wasm_exec.d.ts +1 -0
  221. package/templates/wasm/public/wasm_exec.d.ts.map +1 -0
  222. package/templates/wasm/src/App.d.ts +2 -0
  223. package/templates/wasm/src/App.d.ts.map +1 -0
  224. package/templates/wasm/src/App.js +381 -0
  225. package/templates/wasm/src/client.d.ts +2 -0
  226. package/templates/wasm/src/client.d.ts.map +1 -0
  227. package/templates/wasm/src/client.js +210 -0
  228. package/templates/wasm/src/index.d.ts +2 -0
  229. package/templates/wasm/src/index.d.ts.map +1 -0
  230. package/templates/wasm/src/index.js +20 -0
  231. package/templates/wasm/src/server.d.ts +2 -0
  232. package/templates/wasm/src/server.d.ts.map +1 -0
  233. package/templates/wasm/src/server.js +131 -0
  234. package/templates/wasm/vite.config.d.ts +3 -0
  235. package/templates/wasm/vite.config.d.ts.map +1 -0
  236. package/templates/wasm/vite.config.js +36 -0
  237. package/templates/wasm/wasm-loader.d.ts +6 -0
  238. package/templates/wasm/wasm-loader.d.ts.map +1 -0
  239. package/dist/renderer-BL3gq8cW.cjs +0 -2
  240. package/dist/renderer-BL3gq8cW.cjs.map +0 -1
  241. package/dist/renderer-Dyy-o05F.js +0 -52
  242. package/dist/renderer-Dyy-o05F.js.map +0 -1
  243. package/dist/server-renderer-C1WXH-zV.js.map +0 -1
  244. package/dist/server-renderer-Chs-nmJm.cjs +0 -2
  245. package/dist/server-renderer-Chs-nmJm.cjs.map +0 -1
@@ -0,0 +1,17 @@
1
+ declare function jsx(type: any, props: any, ...children: any[]): {
2
+ type: any;
3
+ props: any;
4
+ };
5
+ declare function jsx(type: any, props: any, ...args: any[]): {
6
+ type: any;
7
+ props: any;
8
+ };
9
+ declare function jsx(type: any, props: any, ...args: any[]): {
10
+ type: any;
11
+ props: any;
12
+ };
13
+ declare function hydrate(element: any, container: any): void;
14
+ declare function hydrate(element: any, container: any): Promise<void>;
15
+ declare function hydrate(element: any, container: any): Promise<void>;
16
+ declare const Fragment: typeof Fragment;
17
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["client.js"],"names":[],"mappings":"AAGA;;;EAMC;;;;;;;;;AAMD,6DAsCC;;;AAzCD,wCAA8C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["server.ts"],"names":[],"mappings":""}
@@ -1,226 +1,244 @@
1
- import express from 'express';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- import fs from 'fs';
5
- // Fix imports by using only the exports that exist
6
- import {
7
- renderToString,
8
- jsx,
9
- requestLogger,
10
- errorHandler,
11
- notFoundHandler,
12
- rateLimit
13
- } from './lib/frontend-hamroun.js';
14
- import dotenv from 'dotenv';
15
- import compression from 'compression';
16
- import cors from 'cors';
17
-
18
- // Load environment variables
19
- dotenv.config();
20
-
21
- // Get __dirname equivalent in ESM
22
- const __filename = fileURLToPath(import.meta.url);
23
- const __dirname = path.dirname(__filename);
24
-
25
- // Create Express app
26
- const app = express();
27
- const PORT = process.env.PORT || 3000;
28
-
29
- // Middleware
30
- app.use(compression()); // Compress responses
31
- app.use(express.json()); // Parse JSON requests
32
- app.use(express.urlencoded({ extended: true })); // Parse URL-encoded requests
33
- app.use(cors()); // Enable CORS
34
- app.use(requestLogger); // Log all requests
35
-
36
- // Add rate limiting to prevent abuse
37
- app.use(rateLimit({
38
- windowMs: 15 * 60 * 1000, // 15 minutes
39
- max: 100 // limit each IP to 100 requests per windowMs
40
- }));
41
-
42
- // Serve static files from the public directory
43
- // Important: This middleware should be defined BEFORE your catch-all route
44
- app.use(express.static(path.join(__dirname, 'public')));
45
-
46
- // Add correct MIME type for client.js module
47
- app.get('/client.js', (req, res) => {
48
- res.type('application/javascript').sendFile(path.join(__dirname, 'public', 'client.js'));
49
- });
50
-
51
- // Serve WebAssembly files with correct MIME type
52
- app.get('*.wasm', (req, res, next) => {
53
- res.type('application/wasm');
54
- next();
55
- });
56
-
57
- // Add API routes example
58
- app.get('/api/hello', (req, res) => {
59
- res.json({
60
- message: 'Hello from the API!',
61
- serverTime: new Date().toISOString()
62
- });
63
- });
64
-
65
- // Find the pages directory
66
- const getPagesDirectory = () => {
67
- return path.join(__dirname, 'pages');
68
- };
69
-
70
- // Helper to check if a file exists
71
- const fileExists = async (filePath) => {
72
- try {
73
- await fs.promises.access(filePath);
74
- return true;
75
- } catch {
76
- return false;
77
- }
78
- };
79
-
80
- // Map URL path to component path
81
- const getComponentPath = async (urlPath) => {
82
- const pagesDir = getPagesDirectory();
83
-
84
- // Handle root path
85
- if (urlPath === '/') {
86
- const indexPath = path.join(pagesDir, 'index.js');
87
- if (await fileExists(indexPath)) {
88
- return {
89
- componentPath: indexPath,
90
- params: {}
91
- };
92
- }
93
- }
94
-
95
- // Try direct match (e.g., /about -> /pages/about.js)
96
- const possibleExtensions = ['.js'];
97
-
98
- for (const ext of possibleExtensions) {
99
- const directPath = path.join(pagesDir, `${urlPath.slice(1)}${ext}`);
100
- if (await fileExists(directPath)) {
101
- return {
102
- componentPath: directPath,
103
- params: {}
104
- };
105
- }
106
- }
107
-
108
- // Try directory index (e.g., /about -> /pages/about/index.js)
109
- for (const ext of possibleExtensions) {
110
- const dirIndexPath = path.join(pagesDir, urlPath.slice(1), `index${ext}`);
111
- if (await fileExists(dirIndexPath)) {
112
- return {
113
- componentPath: dirIndexPath,
114
- params: {}
115
- };
116
- }
117
- }
118
-
119
- // No match found
120
- return null;
121
- };
122
-
123
- // Handle all GET routes with SSR - explicitly exclude /public/ paths
124
- app.get('*', async (req, res, next) => {
125
- try {
126
- // Skip API routes
127
- if (req.path.startsWith('/api/')) {
128
- return next();
129
- }
130
-
131
- // Skip static assets - check file extensions that should be handled as static
132
- if (req.path.match(/\.(js|css|ico|png|jpg|jpeg|gif|svg|wasm|txt|pdf|json|map)$/)) {
133
- return next();
134
- }
135
-
136
- // Try to find the matching component
137
- const routeResult = await getComponentPath(req.path);
138
-
139
- if (!routeResult) {
140
- return res.status(404).send(`
1
+ import express from 'express';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
+ import { renderToString } from 'frontend-hamroun';
5
+ import { Database } from 'frontend-hamroun';
6
+ import { AuthService } from 'frontend-hamroun';
7
+ import { requestLogger, errorHandler, notFoundHandler, rateLimit } from 'frontend-hamroun';
8
+ import dotenv from 'dotenv';
9
+ // Load environment variables
10
+ dotenv.config();
11
+ // Get directory name in ESM
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = dirname(__filename);
14
+ // Create Express app
15
+ const app = express();
16
+ const port = process.env.PORT ? parseInt(process.env.PORT) : 3000;
17
+ // Add middleware
18
+ app.use(express.json());
19
+ app.use(express.urlencoded({ extended: true }));
20
+ app.use(requestLogger);
21
+ // Rate limiting for API routes
22
+ app.use('/api', rateLimit({
23
+ windowMs: 15 * 60 * 1000, // 15 minutes
24
+ max: 100 // limit each IP to 100 requests per windowMs
25
+ }));
26
+ // Configure database if connection string is provided
27
+ let db = null;
28
+ if (process.env.DATABASE_URL) {
29
+ db = new Database({
30
+ url: process.env.DATABASE_URL,
31
+ type: (process.env.DATABASE_TYPE || 'mongodb')
32
+ });
33
+ // Connect to database
34
+ try {
35
+ await db.connect();
36
+ console.log('Database connected successfully');
37
+ }
38
+ catch (error) {
39
+ console.error('Database connection failed:', error);
40
+ }
41
+ }
42
+ // Configure auth if secret is provided
43
+ let auth = null;
44
+ if (process.env.JWT_SECRET) {
45
+ auth = new AuthService({
46
+ secret: process.env.JWT_SECRET,
47
+ expiresIn: process.env.JWT_EXPIRES_IN || '24h'
48
+ });
49
+ // Add auth middleware
50
+ app.use(auth.initialize());
51
+ // Example protected route
52
+ app.get('/api/protected', auth.requireAuth(), (req, res) => {
53
+ res.json({ message: 'Protected route accessed successfully' });
54
+ });
55
+ // Example role-based protection
56
+ app.get('/api/admin', auth.requireRoles(['admin']), (req, res) => {
57
+ res.json({ message: 'Admin route accessed successfully' });
58
+ });
59
+ // Login route
60
+ app.post('/api/login', async (req, res) => {
61
+ const { username, password } = req.body;
62
+ // In a real app, fetch user from database
63
+ const user = { id: 1, username, roles: ['user'] };
64
+ const token = auth.generateToken(user);
65
+ res.json({ token, user: { id: user.id, username: user.username, roles: user.roles } });
66
+ });
67
+ }
68
+ // Serve static files from public directory
69
+ app.use(express.static(join(__dirname, 'public')));
70
+ // API endpoint example
71
+ app.get('/api/page-data', (req, res) => {
72
+ res.json({
73
+ title: 'Server-side Data',
74
+ content: 'This data was fetched from the server',
75
+ timestamp: new Date().toISOString()
76
+ });
77
+ });
78
+ // Meta tag generation function (using local logic for simplicity)
79
+ async function generateMetaTags(pageContent) {
80
+ // Extract title from page content
81
+ const title = pageContent.split('\n')[0].replace(/[#*]/g, '').trim() ||
82
+ 'Frontend Hamroun SSR Page';
83
+ // Generate description from content
84
+ const description = pageContent.substring(0, 150) + '...';
85
+ // Extract keywords
86
+ const keywords = pageContent
87
+ .toLowerCase()
88
+ .replace(/[^\w\s]/g, '')
89
+ .split(/\s+/)
90
+ .filter(w => w.length > 3)
91
+ .slice(0, 5)
92
+ .join(', ');
93
+ return {
94
+ title,
95
+ description,
96
+ keywords
97
+ };
98
+ }
99
+ // Helper function to check if file exists
100
+ async function fileExists(path) {
101
+ try {
102
+ const fs = await import('fs/promises');
103
+ await fs.access(path);
104
+ return true;
105
+ }
106
+ catch {
107
+ return false;
108
+ }
109
+ }
110
+ // Implement basic SSR without relying on complex server functionality
111
+ app.get('*', async (req, res) => {
112
+ try {
113
+ // Import the page component
114
+ const pagesDir = join(__dirname, 'src', 'pages');
115
+ let componentPath;
116
+ // Map URL path to component file
117
+ if (req.path === '/') {
118
+ componentPath = join(pagesDir, 'index.js');
119
+ }
120
+ else {
121
+ componentPath = join(pagesDir, `${req.path}.js`);
122
+ // Check if it's a directory with index.js
123
+ if (!await fileExists(componentPath)) {
124
+ componentPath = join(pagesDir, req.path, 'index.js');
125
+ }
126
+ }
127
+ // If component doesn't exist, return 404
128
+ if (!await fileExists(componentPath)) {
129
+ return res.status(404).send(`
141
130
  <!DOCTYPE html>
142
131
  <html>
143
132
  <head>
144
133
  <title>404 - Page Not Found</title>
145
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
146
- <link href="/styles.css" rel="stylesheet" type="text/css">
147
134
  </head>
148
135
  <body>
149
- <div id="app">
150
- <div class="container">
151
- <h1>404 - Page Not Found</h1>
152
- <p>The page you requested could not be found.</p>
153
- <a href="/" class="button">Go to Home</a>
154
- </div>
155
- </div>
156
- <script id="__APP_DATA__" type="application/json">${JSON.stringify({
157
- path: req.path,
158
- error: 'not_found'
159
- })}</script>
160
- <script type="module" src="/client.js"></script>
136
+ <h1>404 - Page Not Found</h1>
137
+ <p>The page you requested does not exist.</p>
161
138
  </body>
162
139
  </html>
163
- `);
164
- }
165
-
166
- // Import the component
167
- try {
168
- const componentUrl = `file://${routeResult.componentPath}`;
169
- const moduleImport = await import(componentUrl);
170
- const PageComponent = moduleImport.default || moduleImport;
171
-
172
- if (!PageComponent || typeof PageComponent !== 'function') {
173
- throw new Error(`Invalid component in ${routeResult.componentPath}`);
174
- }
175
-
176
- // Create props with route data
177
- const initialProps = {
178
- params: routeResult.params,
179
- path: req.path,
180
- query: req.query,
181
- api: {
182
- serverTime: new Date().toISOString()
183
- }
184
- };
185
-
186
- // Render the component
187
- const result = PageComponent(initialProps);
188
- let html = renderToString(result);
189
-
190
- // Send complete HTML with hydration data
191
- res.send(`
192
- <!DOCTYPE html>
193
- <html lang="en">
194
- <head>
195
- <meta charset="UTF-8">
196
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
197
- <title>Frontend Hamroun App</title>
198
- <link href="/styles.css" rel="stylesheet" type="text/css">
199
- </head>
200
- <body>
201
- <div id="app">${html}</div>
202
- <script id="__APP_DATA__" type="application/json">${JSON.stringify(initialProps)}</script>
203
- <script type="module" src="/client.js"></script>
204
- </body>
205
- </html>
206
- `);
207
- } catch (error) {
208
- console.error('Error rendering component:', error);
209
- next(error);
210
- }
211
- } catch (error) {
212
- next(error);
213
- }
214
- });
215
-
216
- // Add error handler middleware
217
- app.use(errorHandler);
218
-
219
- // Add 404 handler for API routes
220
- app.use(notFoundHandler);
221
-
222
- // Start the server
223
- app.listen(PORT, () => {
224
- console.log(`Server running at http://localhost:${PORT}`);
225
- console.log(`Mode: ${process.env.NODE_ENV || 'development'}`);
226
- });
140
+ `);
141
+ }
142
+ // Import the component
143
+ const { default: PageComponent } = await import(componentPath);
144
+ // Generate page content for meta tags
145
+ const pageContent = `
146
+ Frontend Hamroun SSR Page
147
+ This is a server-rendered page using the Frontend Hamroun framework.
148
+ Path: ${req.path}
149
+ Timestamp: ${new Date().toISOString()}
150
+ `;
151
+ // Generate meta tags
152
+ const metaTags = await generateMetaTags(pageContent);
153
+ // Render the component to string
154
+ const content = renderToString(PageComponent());
155
+ // Send the HTML response
156
+ res.send(`
157
+ <!DOCTYPE html>
158
+ <html lang="en">
159
+ <head>
160
+ <meta charset="UTF-8">
161
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
162
+
163
+ <!-- Generated Meta Tags -->
164
+ <title>${metaTags.title}</title>
165
+ <meta name="description" content="${metaTags.description}">
166
+ <meta name="keywords" content="${metaTags.keywords}">
167
+
168
+ <!-- Open Graph Meta Tags -->
169
+ <meta property="og:title" content="${metaTags.title}">
170
+ <meta property="og:description" content="${metaTags.description}">
171
+ <meta property="og:type" content="website">
172
+ <meta property="og:url" content="${req.protocol}://${req.get('host')}${req.originalUrl}">
173
+
174
+ <!-- Import Tailwind-like styles for quick styling -->
175
+ <link href="https://cdn.jsdelivr.net/npm/daisyui@3.7.4/dist/full.css" rel="stylesheet" type="text/css" />
176
+ <script src="https://cdn.tailwindcss.com"></script>
177
+
178
+ <!-- Client-side script for hydration -->
179
+ <script type="module" src="/assets/client.js"></script>
180
+ </head>
181
+ <body>
182
+ <div id="app">${content}</div>
183
+
184
+ <!-- Add initial state for hydration -->
185
+ <script>
186
+ window.__INITIAL_STATE__ = ${JSON.stringify({
187
+ path: req.path,
188
+ timestamp: new Date().toISOString(),
189
+ metaTags
190
+ })};
191
+ </script>
192
+ </body>
193
+ </html>
194
+ `);
195
+ }
196
+ catch (error) {
197
+ console.error('Error rendering page:', error);
198
+ res.status(500).send(`
199
+ <!DOCTYPE html>
200
+ <html>
201
+ <head>
202
+ <title>500 - Server Error</title>
203
+ </head>
204
+ <body>
205
+ <h1>500 - Server Error</h1>
206
+ <p>There was an error processing your request.</p>
207
+ ${process.env.NODE_ENV === 'development' ? `<pre>${error.stack}</pre>` : ''}
208
+ </body>
209
+ </html>
210
+ `);
211
+ }
212
+ });
213
+ // Add error handler middleware
214
+ app.use(errorHandler);
215
+ // Add not found handler for API routes that weren't caught
216
+ app.use(notFoundHandler);
217
+ // Graceful shutdown function to close database connections
218
+ function gracefulShutdown() {
219
+ console.log('Shutting down server...');
220
+ // Close database connection if it exists
221
+ if (db) {
222
+ db.disconnect()
223
+ .then(() => console.log('Database disconnected'))
224
+ .catch(err => console.error('Error disconnecting from database:', err))
225
+ .finally(() => process.exit(0));
226
+ }
227
+ else {
228
+ process.exit(0);
229
+ }
230
+ }
231
+ // Start the server
232
+ const server = app.listen(port, () => {
233
+ console.log(`Server running at http://localhost:${port}`);
234
+ console.log(`Available API endpoints:`);
235
+ console.log(` - GET /api/page-data`);
236
+ console.log(` - POST /api/login`);
237
+ if (process.env.JWT_SECRET) {
238
+ console.log(` - GET /api/protected (requires authentication)`);
239
+ console.log(` - GET /api/admin (requires admin role)`);
240
+ }
241
+ });
242
+ // Handle graceful shutdown
243
+ process.on('SIGINT', gracefulShutdown);
244
+ process.on('SIGTERM', gracefulShutdown);
@@ -0,0 +1,2 @@
1
+ export declare function App(): JSX.Element;
2
+ //# sourceMappingURL=App.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["App.tsx"],"names":[],"mappings":"AAKA,wBAAgB,GAAG,gBAqDlB"}
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "frontend-hamroun/jsx-runtime";
2
+ import { useState, useEffect, useMemo, useRef, createContext } from 'frontend-hamroun';
3
+ // Create a theme context
4
+ const ThemeContext = createContext('light');
5
+ export function App() {
6
+ // Initialize with a default state that works on both server and client
7
+ const [count, setCount] = useState(0);
8
+ const [theme, setTheme] = useState('light');
9
+ const renderCount = useRef(0);
10
+ // Client-side only effect
11
+ useEffect(() => {
12
+ if (typeof window !== 'undefined') {
13
+ renderCount.current += 1;
14
+ console.log('Component rendered', renderCount.current, 'times');
15
+ }
16
+ return () => console.log('Component unmounting');
17
+ }, [count]);
18
+ // Memoized value
19
+ const doubled = useMemo(() => count * 2, [count]);
20
+ return (_jsx(ThemeContext.Provider, { value: theme, children: _jsxs("div", { style: {
21
+ padding: '20px',
22
+ backgroundColor: theme === 'dark' ? '#333' : '#fff',
23
+ color: theme === 'dark' ? '#fff' : '#333'
24
+ }, children: [_jsx("h1", { children: "Server-Side Rendered App" }), _jsxs("div", { children: [_jsx("button", { onClick: () => setCount(count - 1), "data-action": "decrement", children: "-" }), _jsx("span", { style: { margin: '0 10px' }, children: count }), _jsx("button", { onClick: () => setCount(count + 1), "data-action": "increment", children: "+" })] }), _jsxs("p", { children: ["Doubled value: ", doubled] }), typeof window !== 'undefined' && (_jsxs("p", { children: ["Render count: ", renderCount.current] })), _jsxs("button", { onClick: () => setTheme(theme === 'light' ? 'dark' : 'light'), style: { marginTop: '10px' }, children: ["Toggle Theme (", theme, ")"] }), _jsx("script", { dangerouslySetInnerHTML: {
25
+ __html: `window.__INITIAL_STATE__ = ${JSON.stringify({ count: 0, theme: 'light' })};`
26
+ } })] }) }));
27
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["client.ts"],"names":[],"mappings":""}
@@ -0,0 +1,52 @@
1
+ import { hydrate, jsx } from 'frontend-hamroun';
2
+ // Dynamically import the appropriate page component
3
+ async function hydratePage() {
4
+ try {
5
+ // Get initial state from server
6
+ const initialState = window.__INITIAL_STATE__ || {};
7
+ // Get current path
8
+ const path = initialState.route || window.location.pathname;
9
+ const normalizedPath = path === '/' ? '/index' : path;
10
+ // Create path to module
11
+ const modulePath = `.${normalizedPath.replace(/\/$/, '')}.js`;
12
+ try {
13
+ // Dynamically import the component
14
+ const module = await import(`./pages${normalizedPath}.js`).catch(() => import(`./pages${normalizedPath}/index.js`));
15
+ const PageComponent = module.default;
16
+ // Find the root element
17
+ const rootElement = document.getElementById('root');
18
+ if (rootElement && PageComponent) {
19
+ // Hydrate the application with the same params from the server
20
+ hydrate(jsx(PageComponent, { params: initialState.params || {} }), rootElement);
21
+ console.log('Hydration complete');
22
+ }
23
+ else {
24
+ console.error('Could not find root element or page component');
25
+ }
26
+ }
27
+ catch (importError) {
28
+ console.error('Error importing page component:', importError);
29
+ // Fallback to App component if available
30
+ try {
31
+ const { App } = await import('./App.js');
32
+ const rootElement = document.getElementById('root');
33
+ if (rootElement && App) {
34
+ hydrate(jsx(App, {}), rootElement);
35
+ console.log('Fallback hydration complete');
36
+ }
37
+ }
38
+ catch (fallbackError) {
39
+ console.error('Fallback hydration failed:', fallbackError);
40
+ }
41
+ }
42
+ }
43
+ catch (error) {
44
+ console.error('Hydration error:', error);
45
+ }
46
+ }
47
+ // Add global variable for JSX
48
+ window.jsx = jsx;
49
+ // Hydrate when DOM is ready
50
+ document.addEventListener('DOMContentLoaded', hydratePage);
51
+ // Handle client-side navigation (if implemented)
52
+ window.addEventListener('popstate', hydratePage);
@@ -0,0 +1,2 @@
1
+ export default function HomePage(): JSX.Element;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.tsx"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,UAAU,QAAQ,gBA+C/B"}
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "frontend-hamroun/jsx-runtime";
2
+ import { useState, useEffect } from 'frontend-hamroun';
3
+ // This component will be rendered on both server and client
4
+ export default function HomePage() {
5
+ const [count, setCount] = useState(0);
6
+ const [serverTime, setServerTime] = useState('');
7
+ // This effect only runs on the client after hydration
8
+ useEffect(() => {
9
+ // Set server render time (only when hydrating)
10
+ if (!serverTime) {
11
+ setServerTime('Client-side hydration complete');
12
+ }
13
+ // Simple cleanup function
14
+ return () => {
15
+ console.log('Component unmounting');
16
+ };
17
+ }, []);
18
+ return (_jsx("div", { id: "app", children: _jsx("div", { className: "hero min-h-screen bg-base-200", children: _jsx("div", { className: "hero-content text-center", children: _jsxs("div", { className: "max-w-md", children: [_jsx("h1", { className: "text-5xl font-bold", children: "Frontend Hamroun SSR" }), _jsx("p", { className: "py-6", children: "This page was rendered on the server and hydrated on the client." }), _jsxs("div", { className: "my-4 p-4 bg-base-300 rounded-lg", children: [_jsxs("p", { children: ["Counter: ", count] }), _jsx("button", { className: "btn btn-primary mt-2", onClick: () => setCount(count + 1), children: "Increment" })] }), _jsx("div", { className: "text-sm opacity-70 mt-4", children: serverTime ? serverTime : `Server rendered at: ${new Date().toISOString()}` })] }) }) }) }));
19
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["server.ts"],"names":[],"mappings":""}