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
@@ -460,13 +460,19 @@ video {
460
460
  max-width: 1536px;
461
461
  }
462
462
  }
463
+ .m-1 {
464
+ margin: 0.25rem;
465
+ }
466
+ .mx-4 {
467
+ margin-left: 1rem;
468
+ margin-right: 1rem;
469
+ }
463
470
  .mx-auto {
464
471
  margin-left: auto;
465
472
  margin-right: auto;
466
473
  }
467
- .my-4 {
468
- margin-top: 1rem;
469
- margin-bottom: 1rem;
474
+ .mb-1 {
475
+ margin-bottom: 0.25rem;
470
476
  }
471
477
  .mb-2 {
472
478
  margin-bottom: 0.5rem;
@@ -486,12 +492,24 @@ video {
486
492
  .mr-2 {
487
493
  margin-right: 0.5rem;
488
494
  }
489
- .mt-12 {
490
- margin-top: 3rem;
495
+ .mt-2 {
496
+ margin-top: 0.5rem;
497
+ }
498
+ .mt-3 {
499
+ margin-top: 0.75rem;
500
+ }
501
+ .mt-4 {
502
+ margin-top: 1rem;
503
+ }
504
+ .mt-6 {
505
+ margin-top: 1.5rem;
491
506
  }
492
507
  .mt-8 {
493
508
  margin-top: 2rem;
494
509
  }
510
+ .block {
511
+ display: block;
512
+ }
495
513
  .inline-block {
496
514
  display: inline-block;
497
515
  }
@@ -501,11 +519,38 @@ video {
501
519
  .table {
502
520
  display: table;
503
521
  }
522
+ .grid {
523
+ display: grid;
524
+ }
525
+ .h-4 {
526
+ height: 1rem;
527
+ }
528
+ .h-40 {
529
+ height: 10rem;
530
+ }
504
531
  .h-5 {
505
532
  height: 1.25rem;
506
533
  }
507
- .min-h-\[50vh\] {
508
- min-height: 50vh;
534
+ .h-8 {
535
+ height: 2rem;
536
+ }
537
+ .max-h-24 {
538
+ max-height: 6rem;
539
+ }
540
+ .max-h-96 {
541
+ max-height: 24rem;
542
+ }
543
+ .min-h-screen {
544
+ min-height: 100vh;
545
+ }
546
+ .w-1\/2 {
547
+ width: 50%;
548
+ }
549
+ .w-1\/4 {
550
+ width: 25%;
551
+ }
552
+ .w-3\/4 {
553
+ width: 75%;
509
554
  }
510
555
  .w-5 {
511
556
  width: 1.25rem;
@@ -516,8 +561,25 @@ video {
516
561
  .max-w-4xl {
517
562
  max-width: 56rem;
518
563
  }
519
- .max-w-5xl {
520
- max-width: 64rem;
564
+ .flex-grow {
565
+ flex-grow: 1;
566
+ }
567
+ @keyframes pulse {
568
+ 50% {
569
+ opacity: .5;
570
+ }
571
+ }
572
+ .animate-pulse {
573
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
574
+ }
575
+ .list-disc {
576
+ list-style-type: disc;
577
+ }
578
+ .grid-cols-3 {
579
+ grid-template-columns: repeat(3, minmax(0, 1fr));
580
+ }
581
+ .flex-col {
582
+ flex-direction: column;
521
583
  }
522
584
  .items-center {
523
585
  align-items: center;
@@ -525,6 +587,9 @@ video {
525
587
  .justify-between {
526
588
  justify-content: space-between;
527
589
  }
590
+ .gap-2 {
591
+ gap: 0.5rem;
592
+ }
528
593
  .space-x-6 > :not([hidden]) ~ :not([hidden]) {
529
594
  --tw-space-x-reverse: 0;
530
595
  margin-right: calc(1.5rem * var(--tw-space-x-reverse));
@@ -535,15 +600,27 @@ video {
535
600
  margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
536
601
  margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
537
602
  }
603
+ .space-y-4 > :not([hidden]) ~ :not([hidden]) {
604
+ --tw-space-y-reverse: 0;
605
+ margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
606
+ margin-bottom: calc(1rem * var(--tw-space-y-reverse));
607
+ }
538
608
  .divide-y > :not([hidden]) ~ :not([hidden]) {
539
609
  --tw-divide-y-reverse: 0;
540
610
  border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
541
611
  border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
542
612
  }
613
+ .divide-gray-100 > :not([hidden]) ~ :not([hidden]) {
614
+ --tw-divide-opacity: 1;
615
+ border-color: rgb(243 244 246 / var(--tw-divide-opacity, 1));
616
+ }
543
617
  .divide-gray-200 > :not([hidden]) ~ :not([hidden]) {
544
618
  --tw-divide-opacity: 1;
545
619
  border-color: rgb(229 231 235 / var(--tw-divide-opacity, 1));
546
620
  }
621
+ .self-center {
622
+ align-self: center;
623
+ }
547
624
  .overflow-auto {
548
625
  overflow: auto;
549
626
  }
@@ -553,6 +630,12 @@ video {
553
630
  .overflow-x-auto {
554
631
  overflow-x: auto;
555
632
  }
633
+ .overflow-y-auto {
634
+ overflow-y: auto;
635
+ }
636
+ .rounded {
637
+ border-radius: 0.25rem;
638
+ }
556
639
  .rounded-lg {
557
640
  border-radius: 0.5rem;
558
641
  }
@@ -576,6 +659,18 @@ video {
576
659
  --tw-border-opacity: 1;
577
660
  border-color: rgb(229 231 235 / var(--tw-border-opacity, 1));
578
661
  }
662
+ .border-red-200 {
663
+ --tw-border-opacity: 1;
664
+ border-color: rgb(254 202 202 / var(--tw-border-opacity, 1));
665
+ }
666
+ .border-red-500 {
667
+ --tw-border-opacity: 1;
668
+ border-color: rgb(239 68 68 / var(--tw-border-opacity, 1));
669
+ }
670
+ .border-yellow-200 {
671
+ --tw-border-opacity: 1;
672
+ border-color: rgb(254 240 138 / var(--tw-border-opacity, 1));
673
+ }
579
674
  .bg-blue-50 {
580
675
  --tw-bg-opacity: 1;
581
676
  background-color: rgb(239 246 255 / var(--tw-bg-opacity, 1));
@@ -588,14 +683,41 @@ video {
588
683
  --tw-bg-opacity: 1;
589
684
  background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));
590
685
  }
686
+ .bg-gray-200 {
687
+ --tw-bg-opacity: 1;
688
+ background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1));
689
+ }
591
690
  .bg-gray-50 {
592
691
  --tw-bg-opacity: 1;
593
692
  background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));
594
693
  }
694
+ .bg-gray-800 {
695
+ --tw-bg-opacity: 1;
696
+ background-color: rgb(31 41 55 / var(--tw-bg-opacity, 1));
697
+ }
698
+ .bg-green-50 {
699
+ --tw-bg-opacity: 1;
700
+ background-color: rgb(240 253 244 / var(--tw-bg-opacity, 1));
701
+ }
702
+ .bg-red-50 {
703
+ --tw-bg-opacity: 1;
704
+ background-color: rgb(254 242 242 / var(--tw-bg-opacity, 1));
705
+ }
706
+ .bg-red-600 {
707
+ --tw-bg-opacity: 1;
708
+ background-color: rgb(220 38 38 / var(--tw-bg-opacity, 1));
709
+ }
595
710
  .bg-white {
596
711
  --tw-bg-opacity: 1;
597
712
  background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
598
713
  }
714
+ .bg-yellow-50 {
715
+ --tw-bg-opacity: 1;
716
+ background-color: rgb(254 252 232 / var(--tw-bg-opacity, 1));
717
+ }
718
+ .p-2 {
719
+ padding: 0.5rem;
720
+ }
599
721
  .p-4 {
600
722
  padding: 1rem;
601
723
  }
@@ -605,6 +727,14 @@ video {
605
727
  .p-8 {
606
728
  padding: 2rem;
607
729
  }
730
+ .px-2 {
731
+ padding-left: 0.5rem;
732
+ padding-right: 0.5rem;
733
+ }
734
+ .px-3 {
735
+ padding-left: 0.75rem;
736
+ padding-right: 0.75rem;
737
+ }
608
738
  .px-4 {
609
739
  padding-left: 1rem;
610
740
  padding-right: 1rem;
@@ -613,10 +743,18 @@ video {
613
743
  padding-left: 1.5rem;
614
744
  padding-right: 1.5rem;
615
745
  }
746
+ .py-1 {
747
+ padding-top: 0.25rem;
748
+ padding-bottom: 0.25rem;
749
+ }
616
750
  .py-12 {
617
751
  padding-top: 3rem;
618
752
  padding-bottom: 3rem;
619
753
  }
754
+ .py-2 {
755
+ padding-top: 0.5rem;
756
+ padding-bottom: 0.5rem;
757
+ }
620
758
  .py-3 {
621
759
  padding-top: 0.75rem;
622
760
  padding-bottom: 0.75rem;
@@ -625,12 +763,22 @@ video {
625
763
  padding-top: 1rem;
626
764
  padding-bottom: 1rem;
627
765
  }
766
+ .py-6 {
767
+ padding-top: 1.5rem;
768
+ padding-bottom: 1.5rem;
769
+ }
628
770
  .py-8 {
629
771
  padding-top: 2rem;
630
772
  padding-bottom: 2rem;
631
773
  }
632
- .pt-6 {
633
- padding-top: 1.5rem;
774
+ .pb-4 {
775
+ padding-bottom: 1rem;
776
+ }
777
+ .pl-6 {
778
+ padding-left: 1.5rem;
779
+ }
780
+ .pt-4 {
781
+ padding-top: 1rem;
634
782
  }
635
783
  .text-left {
636
784
  text-align: left;
@@ -638,14 +786,14 @@ video {
638
786
  .text-center {
639
787
  text-align: center;
640
788
  }
789
+ .text-2xl {
790
+ font-size: 1.5rem;
791
+ line-height: 2rem;
792
+ }
641
793
  .text-3xl {
642
794
  font-size: 1.875rem;
643
795
  line-height: 2.25rem;
644
796
  }
645
- .text-4xl {
646
- font-size: 2.25rem;
647
- line-height: 2.5rem;
648
- }
649
797
  .text-lg {
650
798
  font-size: 1.125rem;
651
799
  line-height: 1.75rem;
@@ -658,6 +806,10 @@ video {
658
806
  font-size: 1.25rem;
659
807
  line-height: 1.75rem;
660
808
  }
809
+ .text-xs {
810
+ font-size: 0.75rem;
811
+ line-height: 1rem;
812
+ }
661
813
  .font-bold {
662
814
  font-weight: 700;
663
815
  }
@@ -688,6 +840,14 @@ video {
688
840
  --tw-text-opacity: 1;
689
841
  color: rgb(30 64 175 / var(--tw-text-opacity, 1));
690
842
  }
843
+ .text-gray-100 {
844
+ --tw-text-opacity: 1;
845
+ color: rgb(243 244 246 / var(--tw-text-opacity, 1));
846
+ }
847
+ .text-gray-400 {
848
+ --tw-text-opacity: 1;
849
+ color: rgb(156 163 175 / var(--tw-text-opacity, 1));
850
+ }
691
851
  .text-gray-500 {
692
852
  --tw-text-opacity: 1;
693
853
  color: rgb(107 114 128 / var(--tw-text-opacity, 1));
@@ -712,10 +872,30 @@ video {
712
872
  --tw-text-opacity: 1;
713
873
  color: rgb(34 197 94 / var(--tw-text-opacity, 1));
714
874
  }
875
+ .text-red-600 {
876
+ --tw-text-opacity: 1;
877
+ color: rgb(220 38 38 / var(--tw-text-opacity, 1));
878
+ }
879
+ .text-red-700 {
880
+ --tw-text-opacity: 1;
881
+ color: rgb(185 28 28 / var(--tw-text-opacity, 1));
882
+ }
883
+ .text-red-800 {
884
+ --tw-text-opacity: 1;
885
+ color: rgb(153 27 27 / var(--tw-text-opacity, 1));
886
+ }
715
887
  .text-white {
716
888
  --tw-text-opacity: 1;
717
889
  color: rgb(255 255 255 / var(--tw-text-opacity, 1));
718
890
  }
891
+ .text-yellow-600 {
892
+ --tw-text-opacity: 1;
893
+ color: rgb(202 138 4 / var(--tw-text-opacity, 1));
894
+ }
895
+ .text-yellow-700 {
896
+ --tw-text-opacity: 1;
897
+ color: rgb(161 98 7 / var(--tw-text-opacity, 1));
898
+ }
719
899
  .underline {
720
900
  text-decoration-line: underline;
721
901
  }
@@ -729,6 +909,14 @@ video {
729
909
  --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
730
910
  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
731
911
  }
912
+ .shadow-sm {
913
+ --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
914
+ --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
915
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
916
+ }
917
+ .filter {
918
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
919
+ }
732
920
  .transition-colors {
733
921
  transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
734
922
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
@@ -755,14 +943,33 @@ body {
755
943
  --tw-bg-opacity: 1;
756
944
  background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1));
757
945
  }
946
+ .hover\:bg-gray-300:hover {
947
+ --tw-bg-opacity: 1;
948
+ background-color: rgb(209 213 219 / var(--tw-bg-opacity, 1));
949
+ }
758
950
  .hover\:bg-gray-50:hover {
759
951
  --tw-bg-opacity: 1;
760
952
  background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));
761
953
  }
954
+ .hover\:bg-red-700:hover {
955
+ --tw-bg-opacity: 1;
956
+ background-color: rgb(185 28 28 / var(--tw-bg-opacity, 1));
957
+ }
958
+ .hover\:text-blue-100:hover {
959
+ --tw-text-opacity: 1;
960
+ color: rgb(219 234 254 / var(--tw-text-opacity, 1));
961
+ }
762
962
  .hover\:text-blue-800:hover {
763
963
  --tw-text-opacity: 1;
764
964
  color: rgb(30 64 175 / var(--tw-text-opacity, 1));
765
965
  }
966
+ .hover\:text-red-600:hover {
967
+ --tw-text-opacity: 1;
968
+ color: rgb(220 38 38 / var(--tw-text-opacity, 1));
969
+ }
766
970
  .hover\:underline:hover {
767
971
  text-decoration-line: underline;
972
+ }
973
+ .disabled\:opacity-50:disabled {
974
+ opacity: 0.5;
768
975
  }
@@ -2,7 +2,7 @@ import express, { Request, Response, NextFunction } from 'express';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import fs from 'fs/promises';
5
- import { existsSync } from 'fs';
5
+ import { existsSync, PathLike } from 'fs';
6
6
  import dotenv from 'dotenv';
7
7
  import { createServer } from 'http';
8
8
  import { Server as SocketServer } from 'socket.io';
@@ -82,7 +82,7 @@ app.get('/api/users/:id', (req, res) => {
82
82
  });
83
83
 
84
84
  // Setup esbuild
85
- let esbuildContext;
85
+ let esbuildContext: esbuild.BuildContext<{ entryPoints: string[]; bundle: true; format: "esm"; outdir: string; sourcemap: boolean; minify: boolean; jsx: "transform"; jsxFactory: string; jsxFragment: string; external: string[]; define: { 'process.env.NODE_ENV': string; }; loader: { '.tsx': "tsx"; '.ts': "ts"; '.jsx': "jsx"; '.js': "js"; '.css': "css"; '.json': "json"; '.png': "file"; '.jpg': "file"; '.svg': "file"; }; plugins: { name: string; setup(build: esbuild.PluginBuild): void; }[]; }>;
86
86
 
87
87
  async function setupEsbuild() {
88
88
  const buildDir = path.join(__dirname, 'build');
@@ -101,7 +101,7 @@ async function setupEsbuild() {
101
101
  // Create a plugin to process CSS with Tailwind
102
102
  postcssPlugin = {
103
103
  name: 'postcss-tailwind',
104
- setup(build) {
104
+ setup(build: esbuild.PluginBuild) {
105
105
  build.onLoad({ filter: /\.css$/ }, async (args) => {
106
106
  const css = await fs.readFile(args.path, 'utf8');
107
107
  try {
@@ -116,11 +116,11 @@ async function setupEsbuild() {
116
116
 
117
117
  return {
118
118
  contents: result.css,
119
- loader: 'css'
119
+ loader: 'css' as esbuild.Loader
120
120
  };
121
121
  } catch (error) {
122
122
  console.error('Error processing CSS with Tailwind:', error);
123
- return { contents: css, loader: 'css' };
123
+ return { contents: css, loader: 'css' as esbuild.Loader };
124
124
  }
125
125
  });
126
126
  }
@@ -209,7 +209,7 @@ if (isDev) {
209
209
  // Determine content type based on file extension
210
210
  const ext = path.extname(filePath);
211
211
  let contentType = 'application/javascript';
212
- let loader: esbuild.Loader = 'js';
212
+ let loader = 'js';
213
213
 
214
214
  switch (ext) {
215
215
  case '.ts':
@@ -233,7 +233,7 @@ if (isDev) {
233
233
 
234
234
  // Transform the file with esbuild
235
235
  const result = await esbuild.transform(source, {
236
- loader,
236
+ loader: loader as esbuild.Loader,
237
237
  sourcemap: 'inline',
238
238
  jsxFactory: 'jsx',
239
239
  jsxFragment: 'Fragment'
@@ -249,7 +249,7 @@ if (isDev) {
249
249
  }
250
250
 
251
251
  // Helper function to check if file exists
252
- async function fileExists(filepath: string): Promise<boolean> {
252
+ async function fileExists(filepath: PathLike) {
253
253
  try {
254
254
  await fs.access(filepath);
255
255
  return true;
@@ -258,8 +258,30 @@ async function fileExists(filepath: string): Promise<boolean> {
258
258
  }
259
259
  }
260
260
 
261
+ // Import server utilities directly without WASM functionality
262
+ import { renderToString } from 'frontend-hamroun';
263
+
264
+ // Simple renderComponent implementation
265
+ async function renderComponent(Component: (arg0: {}) => any, props = {}) {
266
+ try {
267
+ // Create HTML string from component
268
+ const html = renderToString(Component(props));
269
+ return {
270
+ html,
271
+ success: true
272
+ };
273
+ } catch (error) {
274
+ console.error('Error rendering component:', error);
275
+ return {
276
+ html: `<div class="error">Error rendering component</div>`,
277
+ success: false,
278
+ error
279
+ };
280
+ }
281
+ }
282
+
261
283
  // SSR handler for all routes except /api and static files
262
- app.get('*', async (req: Request, res: Response, next: NextFunction) => {
284
+ app.get('*', async (req, res, next) => {
263
285
  // Skip API routes, static assets, and build/src files
264
286
  if (req.path.startsWith('/api/') ||
265
287
  req.path.startsWith('/build/') ||
@@ -323,7 +345,7 @@ app.get('*', async (req: Request, res: Response, next: NextFunction) => {
323
345
  }
324
346
  </style>
325
347
  <script>
326
- window.__INITIAL_STATE__ = ` + JSON.stringify(initialState) + `;
348
+ window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};
327
349
  </script>
328
350
  </head>
329
351
  <body>
@@ -386,8 +408,8 @@ io.on('connection', (socket) => {
386
408
 
387
409
  // Handle file changes for live reload in development
388
410
  if (isDev) {
389
- const chokidar = await import('chokidar');
390
- const watcher = chokidar.watch([
411
+ const { watch } = await import('chokidar');
412
+ const watcher = watch([
391
413
  path.join(__dirname, 'src/**/*'),
392
414
  path.join(__dirname, 'public/**/*')
393
415
  ]);
@@ -417,6 +439,18 @@ if (isDev) {
417
439
  console.log('[Watcher] File watching enabled for development');
418
440
  }
419
441
 
442
+ // API routes
443
+ app.get('/api/posts', (req, res) => {
444
+ const { authorId } = req.query;
445
+
446
+ if (authorId) {
447
+ const filteredPosts = store.posts.filter(p => p.authorId === parseInt( authorId as string));
448
+ return res.json(filteredPosts);
449
+ }
450
+
451
+ res.json(store.posts);
452
+ });
453
+
420
454
  // Start the server
421
455
  httpServer.listen(PORT, () => {
422
456
  console.log(`Server running at http://localhost:${PORT}`);
@@ -0,0 +1,36 @@
1
+ import { jsx, useState, useEffect, useErrorBoundary } from 'frontend-hamroun';
2
+
3
+ interface ErrorBoundaryProps {
4
+ children: any;
5
+ fallback?: (error: Error, reset: () => void) => any;
6
+ }
7
+
8
+ export function ErrorBoundary({ children, fallback }: ErrorBoundaryProps) {
9
+ const [error, resetError] = useErrorBoundary();
10
+
11
+ if (error) {
12
+ if (fallback) {
13
+ return fallback(error, resetError);
14
+ }
15
+
16
+ return (
17
+ <div className="error-boundary p-4 border border-red-500 rounded bg-red-50">
18
+ <h3 className="text-lg font-medium text-red-800 mb-2">Something went wrong</h3>
19
+ <p className="text-red-600 mb-2">{error.message}</p>
20
+ <button
21
+ onClick={resetError}
22
+ className="px-3 py-1 bg-red-600 text-white rounded hover:bg-red-700"
23
+ >
24
+ Try again
25
+ </button>
26
+ {process.env.NODE_ENV !== 'production' && (
27
+ <pre className="mt-3 text-xs overflow-auto p-2 bg-gray-100">
28
+ {error.stack}
29
+ </pre>
30
+ )}
31
+ </div>
32
+ );
33
+ }
34
+
35
+ return children;
36
+ }
@@ -1,44 +1,41 @@
1
1
  import { jsx } from 'frontend-hamroun';
2
- import AppConfig from '../config';
3
2
 
4
- export interface LayoutProps {
3
+ interface LayoutProps {
5
4
  children: any;
6
5
  title?: string;
7
- showNavigation?: boolean;
8
6
  }
9
7
 
10
- export default function Layout({ children, title = AppConfig.title, showNavigation = true }: LayoutProps) {
11
- const { navigation } = AppConfig;
12
-
8
+ export default function Layout({ children, title = 'Frontend Hamroun App' }: LayoutProps) {
13
9
  return (
14
- <div className="container mx-auto px-4 py-8 max-w-5xl">
15
- <header className="mb-8">
16
- <h1 className="text-4xl font-bold text-gray-800 mb-4">{title}</h1>
17
-
18
- {showNavigation && (
19
- <nav className="mb-6">
10
+ <div className="min-h-screen flex flex-col bg-gray-50">
11
+ <header className="bg-blue-600 text-white">
12
+ <div className="container mx-auto px-4 py-4 flex justify-between items-center">
13
+ <h1 className="text-xl font-bold">
14
+ <a href="/" className="hover:text-blue-100">
15
+ {title}
16
+ </a>
17
+ </h1>
18
+
19
+ <nav>
20
20
  <ul className="flex space-x-6">
21
- {navigation.map(item => (
22
- <li key={item.path}>
23
- <a
24
- href={item.path}
25
- className="text-blue-600 hover:text-blue-800 hover:underline transition-colors font-medium"
26
- >
27
- {item.label}
28
- </a>
29
- </li>
30
- ))}
21
+ <li><a href="/" className="hover:text-blue-100">Home</a></li>
22
+ <li><a href="/about" className="hover:text-blue-100">About</a></li>
23
+ {/* WASM demo link removed */}
31
24
  </ul>
32
25
  </nav>
33
- )}
26
+ </div>
34
27
  </header>
35
28
 
36
- <main className="min-h-[50vh]">
29
+ <main className="flex-grow container mx-auto px-4 py-8">
37
30
  {children}
38
31
  </main>
39
32
 
40
- <footer className="mt-12 pt-6 border-t border-gray-200 text-gray-600 text-sm">
41
- <p>{AppConfig.title} &copy; {new Date().getFullYear()}</p>
33
+ <footer className="bg-gray-800 text-white">
34
+ <div className="container mx-auto px-4 py-6">
35
+ <p className="text-center text-gray-400">
36
+ &copy; {new Date().getFullYear()} Frontend Hamroun App. All rights reserved.
37
+ </p>
38
+ </div>
42
39
  </footer>
43
40
  </div>
44
41
  );