frontend-hamroun 1.2.75 → 1.2.77

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 (113) hide show
  1. package/dist/batch/package.json +16 -0
  2. package/dist/client-router/package.json +16 -0
  3. package/dist/component/package.json +16 -0
  4. package/dist/context/package.json +16 -0
  5. package/dist/event-bus/package.json +16 -0
  6. package/dist/forms/package.json +16 -0
  7. package/dist/hooks/package.json +16 -0
  8. package/dist/jsx-runtime/package.json +16 -0
  9. package/dist/lifecycle-events/package.json +16 -0
  10. package/dist/package.json +71 -0
  11. package/dist/render-component/package.json +16 -0
  12. package/dist/renderer/package.json +16 -0
  13. package/dist/router/package.json +16 -0
  14. package/dist/server/package.json +17 -0
  15. package/dist/server/src/client-router.d.ts +60 -0
  16. package/dist/server/src/client-router.js +210 -0
  17. package/dist/server/src/client-router.js.map +1 -0
  18. package/dist/server/src/component.js +1 -1
  19. package/dist/server/src/event-bus.d.ts +23 -0
  20. package/dist/server/src/event-bus.js +75 -0
  21. package/dist/server/src/event-bus.js.map +1 -0
  22. package/dist/server/src/forms.d.ts +40 -0
  23. package/dist/server/src/forms.js +148 -0
  24. package/dist/server/src/forms.js.map +1 -0
  25. package/dist/server/src/hooks.js +2 -2
  26. package/dist/server/src/index.js +19 -11
  27. package/dist/server/src/lifecycle-events.d.ts +108 -0
  28. package/dist/server/src/lifecycle-events.js +177 -0
  29. package/dist/server/src/lifecycle-events.js.map +1 -0
  30. package/dist/server/src/renderComponent.js +1 -1
  31. package/dist/server/src/renderer.js +3 -3
  32. package/dist/server/src/router.d.ts +55 -0
  33. package/dist/server/src/router.js +166 -0
  34. package/dist/server/src/router.js.map +1 -0
  35. package/dist/server/src/server/index.d.ts +75 -2
  36. package/dist/server/src/server/index.js +224 -8
  37. package/dist/server/src/server/index.js.map +1 -1
  38. package/dist/server/src/server/server.js +1 -1
  39. package/dist/server/src/server/templates.d.ts +28 -0
  40. package/dist/server/src/server/templates.js +204 -0
  41. package/dist/server/src/server/templates.js.map +1 -0
  42. package/dist/server/src/server/utils.d.ts +70 -0
  43. package/dist/server/src/server/utils.js +156 -0
  44. package/dist/server/src/server/utils.js.map +1 -0
  45. package/dist/server/src/server-renderer.js +1 -1
  46. package/dist/server/src/store.d.ts +41 -0
  47. package/dist/server/src/store.js +99 -0
  48. package/dist/server/src/store.js.map +1 -0
  49. package/dist/server/src/utils.d.ts +46 -0
  50. package/dist/server/src/utils.js +144 -0
  51. package/dist/server/src/utils.js.map +1 -0
  52. package/dist/server/tsconfig.server.tsbuildinfo +1 -1
  53. package/dist/server-renderer/package.json +16 -0
  54. package/dist/store/package.json +16 -0
  55. package/dist/types/package.json +16 -0
  56. package/dist/utils/package.json +16 -0
  57. package/dist/vdom/package.json +16 -0
  58. package/dist/wasm/package.json +16 -0
  59. package/package.json +14 -13
  60. package/templates/complete-app/build.js +284 -0
  61. package/templates/complete-app/package.json +40 -0
  62. package/templates/complete-app/public/styles.css +345 -0
  63. package/templates/complete-app/src/api/index.js +31 -0
  64. package/templates/complete-app/src/client.js +93 -0
  65. package/templates/complete-app/src/components/App.js +66 -0
  66. package/templates/complete-app/src/components/Footer.js +19 -0
  67. package/templates/complete-app/src/components/Header.js +38 -0
  68. package/templates/complete-app/src/pages/About.js +59 -0
  69. package/templates/complete-app/src/pages/Home.js +54 -0
  70. package/templates/complete-app/src/pages/WasmDemo.js +136 -0
  71. package/templates/complete-app/src/server.js +186 -0
  72. package/templates/complete-app/src/wasm/build.bat +16 -0
  73. package/templates/complete-app/src/wasm/build.sh +16 -0
  74. package/templates/complete-app/src/wasm/example.go +101 -0
  75. package/templates/fullstack-app/build/main.css +225 -15
  76. package/templates/fullstack-app/build/main.css.map +2 -2
  77. package/templates/fullstack-app/build/main.js +657 -372
  78. package/templates/fullstack-app/build/main.js.map +4 -4
  79. package/templates/fullstack-app/build.ts +3 -4
  80. package/templates/fullstack-app/public/styles.css +222 -15
  81. package/templates/fullstack-app/server.ts +46 -12
  82. package/templates/fullstack-app/src/components/ClientHome.tsx +0 -0
  83. package/templates/fullstack-app/src/components/ErrorBoundary.tsx +36 -0
  84. package/templates/fullstack-app/src/components/Layout.tsx +23 -26
  85. package/templates/fullstack-app/src/components/StateDemo.tsx +207 -0
  86. package/templates/fullstack-app/src/components/UserList.tsx +30 -13
  87. package/templates/fullstack-app/src/data/api.ts +173 -38
  88. package/templates/fullstack-app/src/main.tsx +88 -154
  89. package/templates/fullstack-app/src/middleware.ts +28 -0
  90. package/templates/fullstack-app/src/pages/404.tsx +28 -0
  91. package/templates/fullstack-app/src/pages/[id].tsx +0 -0
  92. package/templates/fullstack-app/src/pages/_app.tsx +11 -0
  93. package/templates/fullstack-app/src/pages/_document.tsx +25 -0
  94. package/templates/fullstack-app/src/pages/_error.tsx +45 -0
  95. package/templates/fullstack-app/src/pages/about.tsx +71 -0
  96. package/templates/fullstack-app/src/pages/api/users/[id].ts +73 -0
  97. package/templates/fullstack-app/src/pages/api/users/index.ts +43 -0
  98. package/templates/fullstack-app/src/pages/index.tsx +97 -20
  99. package/templates/fullstack-app/src/pages/users/[id].tsx +153 -0
  100. package/templates/fullstack-app/src/pages/wasm-demo.tsx +1 -0
  101. package/templates/go/example.go +99 -86
  102. package/templates/go-wasm-app/babel.config.js +8 -2
  103. package/templates/go-wasm-app/build.config.js +62 -0
  104. package/templates/go-wasm-app/build.js +218 -0
  105. package/templates/go-wasm-app/package.json +21 -12
  106. package/templates/go-wasm-app/server.js +59 -510
  107. package/templates/go-wasm-app/src/app.js +173 -0
  108. package/templates/go-wasm-app/vite.config.js +16 -5
  109. package/templates/ssr-template/client.js +54 -26
  110. package/templates/ssr-template/server.js +5 -28
  111. package/templates/ssr-template/vite.config.js +21 -5
  112. package/dist/server/wasm.d.ts +0 -7
  113. package/dist/wasm.d.ts +0 -37
@@ -0,0 +1,284 @@
1
+ import { execSync } from 'child_process';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import esbuild from 'esbuild';
5
+ import http from 'http';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ // Get __dirname equivalent in ESM
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ // ANSI color codes for console output
13
+ const colors = {
14
+ reset: "\x1b[0m",
15
+ bright: "\x1b[1m",
16
+ dim: "\x1b[2m",
17
+ cyan: "\x1b[36m",
18
+ green: "\x1b[32m",
19
+ yellow: "\x1b[33m",
20
+ red: "\x1b[31m"
21
+ };
22
+
23
+ // Parse command line arguments
24
+ const args = process.argv.slice(2);
25
+ const isProduction = process.env.NODE_ENV === 'production';
26
+ const wasmOnly = args.includes('--wasm-only');
27
+ const serve = args.includes('--serve');
28
+
29
+ // Helper for logging with colors
30
+ function log(message, type = 'info') {
31
+ const prefix = {
32
+ info: `${colors.bright}${colors.cyan}[INFO]${colors.reset}`,
33
+ success: `${colors.bright}${colors.green}[SUCCESS]${colors.reset}`,
34
+ warning: `${colors.bright}${colors.yellow}[WARNING]${colors.reset}`,
35
+ error: `${colors.bright}${colors.red}[ERROR]${colors.reset}`
36
+ };
37
+
38
+ console.log(`${prefix[type] || prefix.info} ${message}`);
39
+ }
40
+
41
+ // Build configuration
42
+ const config = {
43
+ // Entry points
44
+ entryPoints: {
45
+ client: 'src/client.js',
46
+ server: 'src/server.js'
47
+ },
48
+
49
+ // Output directories
50
+ outDir: {
51
+ client: 'dist/public',
52
+ server: 'dist'
53
+ },
54
+
55
+ // WASM configuration
56
+ wasm: {
57
+ dir: 'src/wasm',
58
+ buildScript: process.platform === 'win32' ? 'build.bat' : './build.sh',
59
+ outputDir: 'dist/public/wasm'
60
+ }
61
+ };
62
+
63
+ // Build WASM modules if they exist
64
+ async function buildWasmModules() {
65
+ const wasmDir = path.join(__dirname, config.wasm.dir);
66
+
67
+ if (!fs.existsSync(wasmDir)) {
68
+ log('No WASM directory found, skipping WASM build', 'warning');
69
+ return;
70
+ }
71
+
72
+ log('Building WebAssembly modules...');
73
+
74
+ try {
75
+ // Execute build script in WASM directory
76
+ execSync(config.wasm.buildScript, {
77
+ cwd: wasmDir,
78
+ stdio: 'inherit'
79
+ });
80
+
81
+ // Ensure output directory exists
82
+ const outputDir = path.join(__dirname, config.wasm.outputDir);
83
+ if (!fs.existsSync(outputDir)) {
84
+ fs.mkdirSync(outputDir, { recursive: true });
85
+ }
86
+
87
+ // Copy WASM files to output directory
88
+ const wasmFiles = fs.readdirSync(wasmDir).filter(file =>
89
+ file.endsWith('.wasm') || file === 'wasm_exec.js');
90
+
91
+ for (const file of wasmFiles) {
92
+ fs.copyFileSync(
93
+ path.join(wasmDir, file),
94
+ path.join(outputDir, file)
95
+ );
96
+ }
97
+
98
+ log(`WebAssembly modules built and copied to ${config.wasm.outputDir}`, 'success');
99
+ } catch (error) {
100
+ log(`Failed to build WebAssembly modules: ${error.message}`, 'error');
101
+ if (!wasmOnly) {
102
+ log('Continuing with rest of build...', 'warning');
103
+ } else {
104
+ process.exit(1);
105
+ }
106
+ }
107
+ }
108
+
109
+ // Build client-side code
110
+ async function buildClient() {
111
+ log('Building client bundle...');
112
+
113
+ try {
114
+ const clientOutDir = path.join(__dirname, config.outDir.client);
115
+
116
+ // Ensure output directory exists
117
+ if (!fs.existsSync(clientOutDir)) {
118
+ fs.mkdirSync(clientOutDir, { recursive: true });
119
+ }
120
+
121
+ // Bundle client code
122
+ await esbuild.build({
123
+ entryPoints: [path.join(__dirname, config.entryPoints.client)],
124
+ bundle: true,
125
+ outfile: path.join(clientOutDir, 'client.js'),
126
+ format: 'esm',
127
+ platform: 'browser',
128
+ target: ['es2020'],
129
+ minify: isProduction,
130
+ sourcemap: !isProduction,
131
+ define: {
132
+ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
133
+ },
134
+ loader: {
135
+ '.js': 'jsx',
136
+ '.jsx': 'jsx'
137
+ }
138
+ });
139
+
140
+ // Copy index.html if it exists
141
+ const indexPath = path.join(__dirname, 'src/index.html');
142
+ if (fs.existsSync(indexPath)) {
143
+ fs.copyFileSync(indexPath, path.join(clientOutDir, 'index.html'));
144
+ }
145
+
146
+ log('Client bundle built successfully', 'success');
147
+ } catch (error) {
148
+ log(`Failed to build client bundle: ${error.message}`, 'error');
149
+ process.exit(1);
150
+ }
151
+ }
152
+
153
+ // Build server-side code
154
+ async function buildServer() {
155
+ log('Building server bundle...');
156
+
157
+ try {
158
+ const serverOutDir = path.join(__dirname, config.outDir.server);
159
+
160
+ // Ensure output directory exists
161
+ if (!fs.existsSync(serverOutDir)) {
162
+ fs.mkdirSync(serverOutDir, { recursive: true });
163
+ }
164
+
165
+ // Bundle server code
166
+ await esbuild.build({
167
+ entryPoints: [path.join(__dirname, config.entryPoints.server)],
168
+ bundle: true,
169
+ outfile: path.join(serverOutDir, 'server.js'),
170
+ format: 'esm',
171
+ platform: 'node',
172
+ target: ['node16'],
173
+ minify: isProduction,
174
+ sourcemap: !isProduction,
175
+ define: {
176
+ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
177
+ },
178
+ loader: {
179
+ '.js': 'jsx',
180
+ '.jsx': 'jsx'
181
+ },
182
+ external: ['express', 'frontend-hamroun', 'compression', 'cors', 'path', 'fs', 'http', 'socket.io']
183
+ });
184
+
185
+ log('Server bundle built successfully', 'success');
186
+ } catch (error) {
187
+ log(`Failed to build server bundle: ${error.message}`, 'error');
188
+ process.exit(1);
189
+ }
190
+ }
191
+
192
+ // Copy static assets like CSS, images, etc.
193
+ function copyStaticAssets() {
194
+ log('Copying static assets...');
195
+
196
+ const publicDir = path.join(__dirname, 'public');
197
+ const outputDir = path.join(__dirname, config.outDir.client);
198
+
199
+ if (fs.existsSync(publicDir)) {
200
+ // Recursive function to copy directory contents
201
+ function copyDir(src, dest) {
202
+ // Create destination directory if it doesn't exist
203
+ if (!fs.existsSync(dest)) {
204
+ fs.mkdirSync(dest, { recursive: true });
205
+ }
206
+
207
+ // Read all files in source directory
208
+ const entries = fs.readdirSync(src, { withFileTypes: true });
209
+
210
+ for (const entry of entries) {
211
+ const srcPath = path.join(src, entry.name);
212
+ const destPath = path.join(dest, entry.name);
213
+
214
+ if (entry.isDirectory()) {
215
+ // Recursively copy subdirectories
216
+ copyDir(srcPath, destPath);
217
+ } else {
218
+ // Copy files
219
+ fs.copyFileSync(srcPath, destPath);
220
+ }
221
+ }
222
+ }
223
+
224
+ copyDir(publicDir, outputDir);
225
+ log('Static assets copied successfully', 'success');
226
+ } else {
227
+ log('No static assets to copy (public directory not found)', 'warning');
228
+ }
229
+ }
230
+
231
+ // Start a development server
232
+ async function startDevServer() {
233
+ log('Starting development server...');
234
+
235
+ // Import server module
236
+ try {
237
+ // Use dynamic import because this is ESM
238
+ const serverModule = await import('./dist/server.js');
239
+ log('Server started. Press Ctrl+C to stop.', 'success');
240
+ } catch (error) {
241
+ log(`Failed to start development server: ${error.message}`, 'error');
242
+ process.exit(1);
243
+ }
244
+ }
245
+
246
+ // Main build function
247
+ async function build() {
248
+ console.log('\n' + '='.repeat(60));
249
+ log(`Building ${isProduction ? 'production' : 'development'} bundle...`);
250
+ console.log('='.repeat(60) + '\n');
251
+
252
+ try {
253
+ // Build WASM first
254
+ await buildWasmModules();
255
+
256
+ // If wasm-only flag is provided, exit after building WASM
257
+ if (wasmOnly) {
258
+ log('WASM-only build completed', 'success');
259
+ return;
260
+ }
261
+
262
+ // Build client and server code
263
+ await Promise.all([
264
+ buildClient(),
265
+ buildServer()
266
+ ]);
267
+
268
+ // Copy static assets
269
+ copyStaticAssets();
270
+
271
+ log('Build completed successfully', 'success');
272
+
273
+ // Start development server if requested
274
+ if (serve) {
275
+ await startDevServer();
276
+ }
277
+ } catch (error) {
278
+ log(`Build failed: ${error.message}`, 'error');
279
+ process.exit(1);
280
+ }
281
+ }
282
+
283
+ // Run the build
284
+ build();
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "complete-app",
3
+ "version": "1.0.0",
4
+ "description": "Complete Frontend Hamroun App with all features: SSR, WASM, API routes, and more",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "cross-env NODE_ENV=development node build.js --serve",
8
+ "build": "cross-env NODE_ENV=production node build.js",
9
+ "build:wasm": "node build.js --wasm-only",
10
+ "start": "cross-env NODE_ENV=production node dist/server.js",
11
+ "test": "vitest run",
12
+ "wasm:build": "cd src/wasm && ./build.sh"
13
+ },
14
+ "dependencies": {
15
+ "compression": "^1.7.4",
16
+ "cors": "^2.8.5",
17
+ "dotenv": "^16.3.1",
18
+ "express": "^4.18.2",
19
+ "frontend-hamroun": "latest",
20
+ "jsonwebtoken": "^9.0.2",
21
+ "socket.io": "^4.7.2"
22
+ },
23
+ "devDependencies": {
24
+ "@types/compression": "^1.7.3",
25
+ "@types/cors": "^2.8.14",
26
+ "@types/express": "^4.17.18",
27
+ "@types/jsonwebtoken": "^9.0.3",
28
+ "@types/node": "^20.8.2",
29
+ "autoprefixer": "^10.4.14",
30
+ "chokidar": "^3.5.3",
31
+ "cross-env": "^7.0.3",
32
+ "esbuild": "^0.19.5",
33
+ "postcss": "^8.4.27",
34
+ "tailwindcss": "^3.3.3",
35
+ "vitest": "^0.34.6"
36
+ },
37
+ "engines": {
38
+ "node": ">=16.0.0"
39
+ }
40
+ }
@@ -0,0 +1,345 @@
1
+ /* Base styles */
2
+ :root {
3
+ --primary-color: #4a89dc;
4
+ --secondary-color: #5d9cec;
5
+ --accent-color: #967adc;
6
+ --success-color: #8cc152;
7
+ --error-color: #da4453;
8
+ --background-color: #f5f7fa;
9
+ --text-color: #434a54;
10
+ --border-color: #e6e9ed;
11
+ }
12
+
13
+ * {
14
+ box-sizing: border-box;
15
+ margin: 0;
16
+ padding: 0;
17
+ }
18
+
19
+ body {
20
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
21
+ line-height: 1.5;
22
+ color: var(--text-color);
23
+ background-color: var(--background-color);
24
+ }
25
+
26
+ a {
27
+ color: var(--primary-color);
28
+ text-decoration: none;
29
+ }
30
+
31
+ a:hover {
32
+ text-decoration: underline;
33
+ }
34
+
35
+ button {
36
+ cursor: pointer;
37
+ background-color: var(--primary-color);
38
+ color: white;
39
+ border: none;
40
+ padding: 0.5rem 1rem;
41
+ border-radius: 4px;
42
+ font-size: 1rem;
43
+ transition: background-color 0.2s;
44
+ }
45
+
46
+ button:hover {
47
+ background-color: var(--secondary-color);
48
+ }
49
+
50
+ input, textarea {
51
+ padding: 0.5rem;
52
+ border: 1px solid var(--border-color);
53
+ border-radius: 4px;
54
+ font-size: 1rem;
55
+ width: 100%;
56
+ }
57
+
58
+ /* Layout */
59
+ .app {
60
+ display: flex;
61
+ flex-direction: column;
62
+ min-height: 100vh;
63
+ }
64
+
65
+ .content {
66
+ flex: 1;
67
+ padding: 2rem;
68
+ max-width: 1200px;
69
+ margin: 0 auto;
70
+ width: 100%;
71
+ }
72
+
73
+ /* Header */
74
+ .app-header {
75
+ background-color: white;
76
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
77
+ padding: 1rem 2rem;
78
+ display: flex;
79
+ justify-content: space-between;
80
+ align-items: center;
81
+ }
82
+
83
+ .logo h1 {
84
+ font-size: 1.5rem;
85
+ color: var(--primary-color);
86
+ }
87
+
88
+ .main-nav ul {
89
+ display: flex;
90
+ list-style: none;
91
+ gap: 2rem;
92
+ }
93
+
94
+ .main-nav a {
95
+ color: var(--text-color);
96
+ font-weight: 500;
97
+ }
98
+
99
+ .main-nav a:hover {
100
+ color: var(--primary-color);
101
+ }
102
+
103
+ /* Footer */
104
+ .app-footer {
105
+ background-color: white;
106
+ box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.05);
107
+ padding: 1.5rem 2rem;
108
+ margin-top: 2rem;
109
+ }
110
+
111
+ .footer-content {
112
+ display: flex;
113
+ justify-content: space-between;
114
+ align-items: center;
115
+ max-width: 1200px;
116
+ margin: 0 auto;
117
+ }
118
+
119
+ .footer-links {
120
+ display: flex;
121
+ gap: 1rem;
122
+ }
123
+
124
+ /* Home page */
125
+ .hero {
126
+ text-align: center;
127
+ margin-bottom: 3rem;
128
+ }
129
+
130
+ .hero h1 {
131
+ font-size: 2.5rem;
132
+ margin-bottom: 0.5rem;
133
+ color: var(--primary-color);
134
+ }
135
+
136
+ .subtitle {
137
+ font-size: 1.2rem;
138
+ color: #666;
139
+ margin-bottom: 2rem;
140
+ }
141
+
142
+ .features {
143
+ display: grid;
144
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
145
+ gap: 2rem;
146
+ margin-top: 3rem;
147
+ }
148
+
149
+ .feature {
150
+ background-color: white;
151
+ border-radius: 8px;
152
+ padding: 1.5rem;
153
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
154
+ transition: transform 0.2s, box-shadow 0.2s;
155
+ }
156
+
157
+ .feature:hover {
158
+ transform: translateY(-5px);
159
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
160
+ }
161
+
162
+ .feature h3 {
163
+ margin-bottom: 1rem;
164
+ color: var(--primary-color);
165
+ }
166
+
167
+ .counter {
168
+ display: flex;
169
+ align-items: center;
170
+ justify-content: center;
171
+ gap: 1rem;
172
+ margin-top: 1rem;
173
+ }
174
+
175
+ .counter button {
176
+ width: 40px;
177
+ height: 40px;
178
+ font-size: 1.2rem;
179
+ display: flex;
180
+ align-items: center;
181
+ justify-content: center;
182
+ padding: 0;
183
+ }
184
+
185
+ .counter span {
186
+ font-size: 1.5rem;
187
+ min-width: 40px;
188
+ text-align: center;
189
+ }
190
+
191
+ .api-button {
192
+ margin-top: 1rem;
193
+ width: 100%;
194
+ }
195
+
196
+ /* About page */
197
+ .about-page {
198
+ max-width: 800px;
199
+ margin: 0 auto;
200
+ }
201
+
202
+ .about-page h1 {
203
+ margin-bottom: 2rem;
204
+ color: var(--primary-color);
205
+ }
206
+
207
+ .about-section {
208
+ margin-bottom: 2rem;
209
+ }
210
+
211
+ .about-section h2 {
212
+ margin-bottom: 1rem;
213
+ color: var(--secondary-color);
214
+ font-size: 1.5rem;
215
+ }
216
+
217
+ .about-section ul {
218
+ margin-left: 1.5rem;
219
+ margin-top: 0.5rem;
220
+ }
221
+
222
+ .about-section li {
223
+ margin-bottom: 0.5rem;
224
+ }
225
+
226
+ /* WASM Demo page */
227
+ .wasm-demo-page {
228
+ max-width: 800px;
229
+ margin: 0 auto;
230
+ }
231
+
232
+ .wasm-demo-page h1 {
233
+ margin-bottom: 2rem;
234
+ color: var(--primary-color);
235
+ }
236
+
237
+ .wasm-status {
238
+ padding: 1rem;
239
+ border-radius: 4px;
240
+ margin-bottom: 2rem;
241
+ }
242
+
243
+ .wasm-status.error {
244
+ background-color: #ffeeee;
245
+ border: 1px solid var(--error-color);
246
+ }
247
+
248
+ .wasm-status.success {
249
+ background-color: #eeffee;
250
+ border: 1px solid var(--success-color);
251
+ }
252
+
253
+ .demo-section {
254
+ background-color: white;
255
+ border-radius: 8px;
256
+ padding: 2rem;
257
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
258
+ }
259
+
260
+ .demo-section h2 {
261
+ margin-bottom: 1rem;
262
+ color: var(--secondary-color);
263
+ font-size: 1.5rem;
264
+ }
265
+
266
+ .calculator {
267
+ display: flex;
268
+ gap: 1rem;
269
+ align-items: center;
270
+ margin-bottom: 2rem;
271
+ flex-wrap: wrap;
272
+ }
273
+
274
+ .calculator input {
275
+ width: 80px;
276
+ }
277
+
278
+ .result {
279
+ background-color: #f7f7f7;
280
+ padding: 0.5rem 1rem;
281
+ border-radius: 4px;
282
+ margin-top: 1rem;
283
+ }
284
+
285
+ .json-parser {
286
+ margin-bottom: 2rem;
287
+ }
288
+
289
+ .json-parser textarea {
290
+ width: 100%;
291
+ margin-bottom: 1rem;
292
+ font-family: monospace;
293
+ }
294
+
295
+ .json-result {
296
+ background-color: #f7f7f7;
297
+ padding: 1rem;
298
+ border-radius: 4px;
299
+ margin-top: 1rem;
300
+ font-family: monospace;
301
+ overflow-x: auto;
302
+ }
303
+
304
+ .function-list {
305
+ list-style: none;
306
+ display: grid;
307
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
308
+ gap: 0.5rem;
309
+ }
310
+
311
+ .function-list li {
312
+ background-color: #f7f7f7;
313
+ padding: 0.5rem;
314
+ border-radius: 4px;
315
+ font-family: monospace;
316
+ }
317
+
318
+ /* Responsive adjustments */
319
+ @media (max-width: 768px) {
320
+ .app-header {
321
+ flex-direction: column;
322
+ gap: 1rem;
323
+ text-align: center;
324
+ }
325
+
326
+ .main-nav ul {
327
+ justify-content: center;
328
+ flex-wrap: wrap;
329
+ gap: 1rem;
330
+ }
331
+
332
+ .footer-content {
333
+ flex-direction: column;
334
+ gap: 1rem;
335
+ text-align: center;
336
+ }
337
+
338
+ .footer-links {
339
+ justify-content: center;
340
+ }
341
+
342
+ .content {
343
+ padding: 1rem;
344
+ }
345
+ }
@@ -0,0 +1,31 @@
1
+ import express from 'express';
2
+
3
+ // Create API router
4
+ const ApiRouter = express.Router();
5
+
6
+ // Random data endpoint
7
+ ApiRouter.get('/random', (req, res) => {
8
+ const value = Math.floor(Math.random() * 1000);
9
+ res.json({ value });
10
+ });
11
+
12
+ // Echo endpoint
13
+ ApiRouter.post('/echo', (req, res) => {
14
+ res.json({
15
+ method: 'POST',
16
+ body: req.body,
17
+ timestamp: new Date().toISOString()
18
+ });
19
+ });
20
+
21
+ // Status endpoint
22
+ ApiRouter.get('/status', (req, res) => {
23
+ res.json({
24
+ status: 'ok',
25
+ timestamp: new Date().toISOString(),
26
+ environment: process.env.NODE_ENV || 'development',
27
+ uptime: process.uptime()
28
+ });
29
+ });
30
+
31
+ export { ApiRouter };