reroute-js 0.2.2 → 0.2.3

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 (188) hide show
  1. package/_/README.md +59 -0
  2. package/_/basic/package.json +23 -0
  3. package/_/basic/src/client/App.tsx +10 -0
  4. package/_/basic/src/client/components/Counter.tsx +15 -0
  5. package/_/basic/src/client/index.html +12 -0
  6. package/_/basic/src/client/index.tsx +5 -0
  7. package/_/basic/src/client/routes/[404].tsx +18 -0
  8. package/_/basic/src/client/routes/about.tsx +25 -0
  9. package/_/basic/src/client/routes/index.tsx +57 -0
  10. package/_/basic/src/index.ts +20 -0
  11. package/_/basic/tsconfig.json +26 -0
  12. package/_/blog/package.json +23 -0
  13. package/_/blog/src/client/App.tsx +10 -0
  14. package/_/blog/src/client/components/Counter.tsx +14 -0
  15. package/_/blog/src/client/components/RecentPosts.tsx +90 -0
  16. package/_/blog/src/client/index.html +13 -0
  17. package/_/blog/src/client/index.tsx +5 -0
  18. package/_/blog/src/client/routes/[404].tsx +21 -0
  19. package/_/blog/src/client/routes/about.tsx +31 -0
  20. package/_/blog/src/client/routes/blog/[404].tsx +21 -0
  21. package/_/blog/src/client/routes/blog/[layout].tsx +84 -0
  22. package/_/blog/src/client/routes/blog/[slug].tsx +11 -0
  23. package/_/blog/src/client/routes/blog/content/1-hello-world.tsx +27 -0
  24. package/_/blog/src/client/routes/blog/content/2-what-is-reroute.tsx +31 -0
  25. package/_/blog/src/client/routes/blog/index.tsx +70 -0
  26. package/_/blog/src/client/routes/index.tsx +63 -0
  27. package/_/blog/src/index.ts +20 -0
  28. package/_/blog/tsconfig.json +26 -0
  29. package/_/store/package.json +25 -0
  30. package/_/store/src/client/App.tsx +17 -0
  31. package/_/store/src/client/components/Header.tsx +40 -0
  32. package/_/store/src/client/components/ProductCard.tsx +51 -0
  33. package/_/store/src/client/index.html +17 -0
  34. package/_/store/src/client/index.tsx +7 -0
  35. package/_/store/src/client/lib/api.ts +153 -0
  36. package/_/store/src/client/routes/[404].tsx +63 -0
  37. package/_/store/src/client/routes/categories/[category].tsx +223 -0
  38. package/_/store/src/client/routes/categories/index.tsx +187 -0
  39. package/_/store/src/client/routes/index.tsx +126 -0
  40. package/_/store/src/client/routes/products/[id].tsx +233 -0
  41. package/_/store/src/client/routes/products/index.tsx +261 -0
  42. package/_/store/src/client/theme.css +306 -0
  43. package/_/store/src/index.ts +19 -0
  44. package/_/store/tsconfig.json +26 -0
  45. package/{packages/cli/bin.ts → cli/bin.d.ts} +1 -1
  46. package/cli/bin.d.ts.map +1 -0
  47. package/cli/bin.js +878 -0
  48. package/cli/bin.js.map +15 -0
  49. package/cli/index.d.ts +2 -0
  50. package/cli/index.d.ts.map +1 -0
  51. package/cli/index.js +147 -0
  52. package/cli/index.js.map +10 -0
  53. package/cli/src/cli.d.ts +8 -0
  54. package/cli/src/cli.d.ts.map +1 -0
  55. package/cli/src/commands/build.d.ts +8 -0
  56. package/cli/src/commands/build.d.ts.map +1 -0
  57. package/cli/src/commands/dev.d.ts +8 -0
  58. package/cli/src/commands/dev.d.ts.map +1 -0
  59. package/cli/src/commands/gen.d.ts +3 -0
  60. package/cli/src/commands/gen.d.ts.map +1 -0
  61. package/cli/src/commands/init.d.ts +8 -0
  62. package/cli/src/commands/init.d.ts.map +1 -0
  63. package/cli/src/libs/index.d.ts +2 -0
  64. package/cli/src/libs/index.d.ts.map +1 -0
  65. package/cli/src/libs/tailwind.d.ts +45 -0
  66. package/cli/src/libs/tailwind.d.ts.map +1 -0
  67. package/core/index.d.ts +2 -0
  68. package/core/index.d.ts.map +1 -0
  69. package/core/index.js +1117 -0
  70. package/core/index.js.map +25 -0
  71. package/core/src/bundler/hash.d.ts +2 -0
  72. package/core/src/bundler/hash.d.ts.map +1 -0
  73. package/core/src/bundler/index.d.ts +3 -0
  74. package/core/src/bundler/index.d.ts.map +1 -0
  75. package/core/src/bundler/transpile.d.ts +4 -0
  76. package/core/src/bundler/transpile.d.ts.map +1 -0
  77. package/core/src/content/discovery.d.ts +5 -0
  78. package/core/src/content/discovery.d.ts.map +1 -0
  79. package/core/src/content/index.d.ts +4 -0
  80. package/core/src/content/index.d.ts.map +1 -0
  81. package/core/src/content/metadata.d.ts +9 -0
  82. package/core/src/content/metadata.d.ts.map +1 -0
  83. package/core/src/content/registry.d.ts +2 -0
  84. package/core/src/content/registry.d.ts.map +1 -0
  85. package/core/src/index.d.ts +7 -0
  86. package/core/src/index.d.ts.map +1 -0
  87. package/core/src/ssr/data.d.ts +9 -0
  88. package/core/src/ssr/data.d.ts.map +1 -0
  89. package/core/src/ssr/index.d.ts +4 -0
  90. package/core/src/ssr/index.d.ts.map +1 -0
  91. package/core/src/ssr/modules.d.ts +8 -0
  92. package/core/src/ssr/modules.d.ts.map +1 -0
  93. package/core/src/ssr/render.d.ts +20 -0
  94. package/core/src/ssr/render.d.ts.map +1 -0
  95. package/core/src/ssr/seed.d.ts +2 -0
  96. package/core/src/ssr/seed.d.ts.map +1 -0
  97. package/core/src/template/html.d.ts +4 -0
  98. package/core/src/template/html.d.ts.map +1 -0
  99. package/core/src/template/index.d.ts +2 -0
  100. package/core/src/template/index.d.ts.map +1 -0
  101. package/core/src/types.d.ts +50 -0
  102. package/core/src/types.d.ts.map +1 -0
  103. package/core/src/utils/cache.d.ts +12 -0
  104. package/core/src/utils/cache.d.ts.map +1 -0
  105. package/core/src/utils/compression.d.ts +5 -0
  106. package/core/src/utils/compression.d.ts.map +1 -0
  107. package/core/src/utils/index.d.ts +5 -0
  108. package/core/src/utils/index.d.ts.map +1 -0
  109. package/core/src/utils/mime.d.ts +3 -0
  110. package/core/src/utils/mime.d.ts.map +1 -0
  111. package/core/src/utils/path.d.ts +6 -0
  112. package/core/src/utils/path.d.ts.map +1 -0
  113. package/elysia/index.d.ts +2 -0
  114. package/elysia/index.d.ts.map +1 -0
  115. package/elysia/index.js +1780 -0
  116. package/elysia/index.js.map +32 -0
  117. package/elysia/src/index.d.ts +3 -0
  118. package/elysia/src/index.d.ts.map +1 -0
  119. package/elysia/src/plugin.d.ts +32 -0
  120. package/elysia/src/plugin.d.ts.map +1 -0
  121. package/elysia/src/routes/artifacts.d.ts +3 -0
  122. package/elysia/src/routes/artifacts.d.ts.map +1 -0
  123. package/elysia/src/routes/content.d.ts +3 -0
  124. package/elysia/src/routes/content.d.ts.map +1 -0
  125. package/elysia/src/routes/dev.d.ts +7 -0
  126. package/elysia/src/routes/dev.d.ts.map +1 -0
  127. package/elysia/src/routes/ssr.d.ts +21 -0
  128. package/elysia/src/routes/ssr.d.ts.map +1 -0
  129. package/elysia/src/routes/static.d.ts +19 -0
  130. package/elysia/src/routes/static.d.ts.map +1 -0
  131. package/elysia/src/types.d.ts +31 -0
  132. package/elysia/src/types.d.ts.map +1 -0
  133. package/elysia/src/utils/http.d.ts +5 -0
  134. package/elysia/src/utils/http.d.ts.map +1 -0
  135. package/package.json +2 -2
  136. package/react/index.d.ts +2 -0
  137. package/react/index.d.ts.map +1 -0
  138. package/react/index.js +1152 -0
  139. package/react/index.js.map +23 -0
  140. package/react/src/components/ContentRoute.d.ts +13 -0
  141. package/react/src/components/ContentRoute.d.ts.map +1 -0
  142. package/react/src/components/Link.d.ts +8 -0
  143. package/react/src/components/Link.d.ts.map +1 -0
  144. package/react/src/components/Outlet.d.ts +7 -0
  145. package/react/src/components/Outlet.d.ts.map +1 -0
  146. package/react/src/components/index.d.ts +4 -0
  147. package/react/src/components/index.d.ts.map +1 -0
  148. package/react/src/hooks/index.d.ts +7 -0
  149. package/react/src/hooks/index.d.ts.map +1 -0
  150. package/react/src/hooks/useContent.d.ts +26 -0
  151. package/react/src/hooks/useContent.d.ts.map +1 -0
  152. package/react/src/hooks/useData.d.ts +10 -0
  153. package/react/src/hooks/useData.d.ts.map +1 -0
  154. package/react/src/hooks/useNavigate.d.ts +6 -0
  155. package/react/src/hooks/useNavigate.d.ts.map +1 -0
  156. package/react/src/hooks/useParams.d.ts +6 -0
  157. package/react/src/hooks/useParams.d.ts.map +1 -0
  158. package/react/src/hooks/useRouter.d.ts +7 -0
  159. package/react/src/hooks/useRouter.d.ts.map +1 -0
  160. package/react/src/hooks/useSearchParams.d.ts +6 -0
  161. package/react/src/hooks/useSearchParams.d.ts.map +1 -0
  162. package/react/src/index.d.ts +6 -0
  163. package/react/src/index.d.ts.map +1 -0
  164. package/react/src/providers/ContentProvider.d.ts +35 -0
  165. package/react/src/providers/ContentProvider.d.ts.map +1 -0
  166. package/react/src/providers/RerouteProvider.d.ts +25 -0
  167. package/react/src/providers/RerouteProvider.d.ts.map +1 -0
  168. package/react/src/providers/RouterProvider.d.ts +23 -0
  169. package/react/src/providers/RouterProvider.d.ts.map +1 -0
  170. package/react/src/providers/index.d.ts +4 -0
  171. package/react/src/providers/index.d.ts.map +1 -0
  172. package/react/src/types/any.d.ts +3 -0
  173. package/react/src/types/any.d.ts.map +1 -0
  174. package/react/src/types/index.d.ts +3 -0
  175. package/react/src/types/index.d.ts.map +1 -0
  176. package/react/src/types/router.d.ts +32 -0
  177. package/react/src/types/router.d.ts.map +1 -0
  178. package/react/src/utils/content.d.ts +8 -0
  179. package/react/src/utils/content.d.ts.map +1 -0
  180. package/react/src/utils/head.d.ts +6 -0
  181. package/react/src/utils/head.d.ts.map +1 -0
  182. package/react/src/utils/index.d.ts +3 -0
  183. package/react/src/utils/index.d.ts.map +1 -0
  184. package/CHANGELOG.md +0 -29
  185. package/packages/cli/README.md +0 -264
  186. package/packages/core/README.md +0 -90
  187. package/packages/elysia/README.md +0 -250
  188. package/packages/react/README.md +0 -3
@@ -0,0 +1,70 @@
1
+ import { Link, useContent } from 'reroute-js/react';
2
+
3
+ const meta = {
4
+ title: 'Blog Example',
5
+ description:
6
+ 'See how easy it is to build a blog with custom file-based routing!',
7
+ };
8
+
9
+ function BlogIndex() {
10
+ const { items } = useContent({
11
+ collection: 'blog',
12
+ });
13
+
14
+ return (
15
+ <div style={{ padding: '2rem' }}>
16
+ <h1>Latest Posts</h1>
17
+ <p>Welcome to our blog! Here are our latest posts:</p>
18
+
19
+ <div
20
+ style={{
21
+ marginTop: '2rem',
22
+ display: 'flex',
23
+ flexDirection: 'column',
24
+ gap: '1.5rem',
25
+ }}
26
+ >
27
+ {items.map((post) => (
28
+ <article
29
+ key={post.slug}
30
+ style={{
31
+ padding: '1.5rem',
32
+ border: '1px solid #e0e0e0',
33
+ borderRadius: '8px',
34
+ background: '#fff',
35
+ }}
36
+ >
37
+ <h2 style={{ margin: '0 0 0.5rem 0' }}>
38
+ <Link
39
+ to={post.href}
40
+ style={{ textDecoration: 'none', color: '#0066cc' }}
41
+ >
42
+ {String(post.meta?.title || post.name)}
43
+ </Link>
44
+ </h2>
45
+ <time style={{ fontSize: '0.875rem', color: '#666' }}>
46
+ {post.meta?.date ? String(post.meta.date) : ''}
47
+ </time>
48
+ <p style={{ marginTop: '0.75rem', color: '#333' }}>
49
+ {String(post.meta?.excerpt || '')}
50
+ </p>
51
+ <Link
52
+ to={post.href}
53
+ style={{
54
+ display: 'inline-block',
55
+ marginTop: '0.5rem',
56
+ color: '#0066cc',
57
+ textDecoration: 'none',
58
+ }}
59
+ >
60
+ Read more →
61
+ </Link>
62
+ </article>
63
+ ))}
64
+ </div>
65
+ </div>
66
+ );
67
+ }
68
+
69
+ export default BlogIndex;
70
+ export { meta };
@@ -0,0 +1,63 @@
1
+ import { Link } from 'reroute-js/react';
2
+
3
+ export default function HomePage() {
4
+ return (
5
+ <main style={{ padding: '2rem', maxWidth: '1200px', margin: '0 auto' }}>
6
+ <h1>🏠 Welcome to Reroute!</h1>
7
+ <p>This is the home page of your custom file-based routing system.</p>
8
+
9
+ <section style={{ marginTop: '2rem' }}>
10
+ <h2>Navigation</h2>
11
+ <nav
12
+ style={{
13
+ display: 'flex',
14
+ gap: '1rem',
15
+ flexDirection: 'column',
16
+ maxWidth: '300px',
17
+ }}
18
+ >
19
+ <Link
20
+ to='/about'
21
+ style={{
22
+ padding: '0.5rem 1rem',
23
+ background: '#eee',
24
+ textDecoration: 'none',
25
+ borderRadius: '4px',
26
+ }}
27
+ >
28
+ About
29
+ </Link>
30
+ <Link
31
+ to='/blog'
32
+ style={{
33
+ padding: '0.5rem 1rem',
34
+ background: '#eee',
35
+ textDecoration: 'none',
36
+ borderRadius: '4px',
37
+ }}
38
+ >
39
+ Blog
40
+ </Link>
41
+ </nav>
42
+ </section>
43
+
44
+ <section
45
+ style={{
46
+ marginTop: '2rem',
47
+ padding: '1rem',
48
+ background: '#f5f5f5',
49
+ borderRadius: '8px',
50
+ }}
51
+ >
52
+ <h3>✨ Features</h3>
53
+ <ul>
54
+ <li>Zero-dependency file-based routing</li>
55
+ <li>Client-side navigation (SPA)</li>
56
+ <li>Server-side rendering (SSR)</li>
57
+ <li>Type-safe route params</li>
58
+ <li>Automatic route generation</li>
59
+ </ul>
60
+ </section>
61
+ </main>
62
+ );
63
+ }
@@ -0,0 +1,20 @@
1
+ import { Elysia } from 'elysia';
2
+ import { createElement } from 'react';
3
+ import { reroute } from 'reroute-js/elysia';
4
+ import App from './client/App';
5
+
6
+ const IS_PRODUCTION = Bun.env.NODE_ENV === 'production';
7
+
8
+ const app = new Elysia()
9
+ .use(
10
+ reroute({
11
+ app: createElement(App),
12
+ minify: IS_PRODUCTION,
13
+ }),
14
+ )
15
+ .get('/message', () => ({ message: 'Hello from server' }))
16
+ .listen(Number(Bun.env.PORT || '3000'));
17
+
18
+ console.log(
19
+ `🦊 Elysia Reroute is running at ${app.server?.hostname}:${app.server?.port} on ${Bun.env.NODE_ENV || 'development'}`,
20
+ );
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "isolatedModules": true,
10
+ "baseUrl": ".",
11
+ "rootDir": ".",
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "outDir": "dist/types",
15
+ "emitDeclarationOnly": true,
16
+ "strict": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "noFallthroughCasesInSwitch": true,
20
+ "jsx": "react-jsx",
21
+ "types": ["bun", "node"],
22
+ "paths": {}
23
+ },
24
+ "include": ["packages/**/*.ts", "packages/**/*.tsx", "test/**/*.d.ts"],
25
+ "exclude": ["node_modules", "dist", "test"]
26
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.0.0",
4
+ "description": "A Reroute store application with Tailwind CSS v4",
5
+ "private": true,
6
+ "scripts": {
7
+ "start": "reroute gen && bun src/index.ts",
8
+ "dev": "reroute gen --watch & bun --watch src/index.ts",
9
+ "build": "reroute gen && bun build src/index.ts --target bun --compile --outfile ./dist/app"
10
+ },
11
+ "dependencies": {
12
+ "reroute-js": "latest",
13
+ "elysia": "^1.4.12",
14
+ "react": "^19.2.0",
15
+ "react-dom": "^19.2.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/bun": "latest",
19
+ "@types/react": "^19.2.2",
20
+ "@types/react-dom": "^19.2.2",
21
+ "@tailwindcss/cli": "^4.1.16",
22
+ "tailwindcss": "^4.1.16",
23
+ "typescript": "^5"
24
+ }
25
+ }
@@ -0,0 +1,17 @@
1
+ import { RerouteProvider } from 'reroute-js/react';
2
+ import { artifacts } from '../../.reroute';
3
+
4
+ interface AppProps {
5
+ pathname?: string;
6
+ }
7
+
8
+ export default function App({ pathname }: AppProps = {}) {
9
+ return (
10
+ <RerouteProvider
11
+ from={{
12
+ pathname,
13
+ ...artifacts,
14
+ }}
15
+ />
16
+ );
17
+ }
@@ -0,0 +1,40 @@
1
+ import { Link } from 'reroute-js/react';
2
+
3
+ export default function Header() {
4
+ return (
5
+ <header className='sticky top-0 z-50 bg-white border-b border-gray-200 shadow-sm'>
6
+ <div className='container-custom'>
7
+ <div className='flex items-center justify-between h-16'>
8
+ <Link
9
+ to='/'
10
+ className='flex items-center gap-2 text-2xl font-bold text-primary-600 hover:text-primary-700 transition-colors'
11
+ >
12
+ <span className='text-3xl'>🛒</span>
13
+ <span>Reroute Store</span>
14
+ </Link>
15
+
16
+ <nav className='flex items-center gap-8'>
17
+ <Link
18
+ to='/'
19
+ className='link-muted font-medium hover:text-primary-600'
20
+ >
21
+ Home
22
+ </Link>
23
+ <Link
24
+ to='/products'
25
+ className='link-muted font-medium hover:text-primary-600'
26
+ >
27
+ Products
28
+ </Link>
29
+ <Link
30
+ to='/categories'
31
+ className='link-muted font-medium hover:text-primary-600'
32
+ >
33
+ Categories
34
+ </Link>
35
+ </nav>
36
+ </div>
37
+ </div>
38
+ </header>
39
+ );
40
+ }
@@ -0,0 +1,51 @@
1
+ /** biome-ignore-all lint/a11y/noStaticElementInteractions: who cares, this is just an example */
2
+ import { Link } from 'reroute-js/react';
3
+ import type { Product } from '../lib/api';
4
+
5
+ interface ProductCardProps {
6
+ product: Product;
7
+ }
8
+
9
+ export default function ProductCard({ product }: ProductCardProps) {
10
+ return (
11
+ <div className='card card-hover group'>
12
+ <Link
13
+ to={`/products/${product.id}`}
14
+ className='block h-full no-underline text-inherit'
15
+ >
16
+ <div className='w-full h-60 bg-gray-50 flex items-center justify-center p-4'>
17
+ <img
18
+ src={product.image}
19
+ alt={product.title}
20
+ className='max-w-full max-h-full object-contain'
21
+ loading='lazy'
22
+ style={{ viewTransitionName: `product-image-${product.id}` }}
23
+ />
24
+ </div>
25
+
26
+ <div className='p-4 flex flex-col gap-2 flex-1'>
27
+ <div className='badge-primary'>{product.category}</div>
28
+
29
+ <h3 className='text-base font-semibold text-gray-900 leading-normal line-clamp-2 min-h-[3em] group-hover:text-primary-600 transition-colors'>
30
+ {product.title}
31
+ </h3>
32
+
33
+ <div className='flex justify-between items-center mt-auto pt-2'>
34
+ <span className='text-xl font-bold text-primary-600'>
35
+ ${product.price.toFixed(2)}
36
+ </span>
37
+
38
+ {product.rating && (
39
+ <div className='flex items-center gap-1 text-sm text-gray-600'>
40
+ <span>⭐</span>
41
+ <span>
42
+ {product.rating.rate} ({product.rating.count})
43
+ </span>
44
+ </div>
45
+ )}
46
+ </div>
47
+ </div>
48
+ </Link>
49
+ </div>
50
+ );
51
+ }
@@ -0,0 +1,17 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>{{PROJECT_NAME}}</title>
6
+
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <meta
9
+ name="description"
10
+ content="E-commerce store built with Reroute and FakeStoreAPI"
11
+ />
12
+ </head>
13
+ <body>
14
+ <div id="root"></div>
15
+ <script type="module" src="./index.tsx"></script>
16
+ </body>
17
+ </html>
@@ -0,0 +1,7 @@
1
+ import { hydrateRoot } from 'react-dom/client';
2
+ import App from './App';
3
+
4
+ const rootElement = document.getElementById('root');
5
+
6
+ // biome-ignore lint/style/noNonNullAssertion: All good
7
+ hydrateRoot(rootElement!, <App />);
@@ -0,0 +1,153 @@
1
+ // API client for FakeStoreAPI with SSR support
2
+ // All responses are plain JSON objects to ensure serialization compatibility
3
+
4
+ const API_BASE_URL = 'https://fakestoreapi.com';
5
+
6
+ interface Product {
7
+ id: number;
8
+ title: string;
9
+ price: number;
10
+ description: string;
11
+ category: string;
12
+ image: string;
13
+ rating?: {
14
+ rate: number;
15
+ count: number;
16
+ };
17
+ }
18
+
19
+ interface CartProduct {
20
+ productId: number;
21
+ quantity: number;
22
+ }
23
+
24
+ interface Cart {
25
+ id: number;
26
+ userId: number;
27
+ date: string;
28
+ products: CartProduct[];
29
+ }
30
+
31
+ interface User {
32
+ id: number;
33
+ email: string;
34
+ username: string;
35
+ password: string;
36
+ name: {
37
+ firstname: string;
38
+ lastname: string;
39
+ };
40
+ address: {
41
+ city: string;
42
+ street: string;
43
+ number: number;
44
+ zipcode: string;
45
+ geolocation: {
46
+ lat: string;
47
+ long: string;
48
+ };
49
+ };
50
+ phone: string;
51
+ }
52
+
53
+ interface LoginCredentials {
54
+ username: string;
55
+ password: string;
56
+ }
57
+
58
+ interface LoginResponse {
59
+ token: string;
60
+ }
61
+
62
+ // Helper to ensure responses are serializable
63
+ function ensureSerializable<T>(data: T): T {
64
+ // Deep clone to break any circular references
65
+ return JSON.parse(JSON.stringify(data));
66
+ }
67
+
68
+ // Generic fetch wrapper with error handling
69
+ async function apiFetch<T>(
70
+ endpoint: string,
71
+ options?: RequestInit,
72
+ ): Promise<T> {
73
+ try {
74
+ const response = await fetch(`${API_BASE_URL}${endpoint}`, {
75
+ ...options,
76
+ headers: {
77
+ 'Content-Type': 'application/json',
78
+ ...options?.headers,
79
+ },
80
+ });
81
+
82
+ if (!response.ok) {
83
+ throw new Error(`API error: ${response.status} ${response.statusText}`);
84
+ }
85
+
86
+ const data = await response.json();
87
+ return ensureSerializable(data);
88
+ } catch (error) {
89
+ console.error(`API request failed: ${endpoint}`, error);
90
+ throw error;
91
+ }
92
+ }
93
+
94
+ // Products API
95
+ export async function getAllProducts(): Promise<Product[]> {
96
+ return apiFetch<Product[]>('/products');
97
+ }
98
+
99
+ export async function getProduct(id: number): Promise<Product> {
100
+ return apiFetch<Product>(`/products/${id}`);
101
+ }
102
+
103
+ export async function getProductsByCategory(
104
+ category: string,
105
+ ): Promise<Product[]> {
106
+ return apiFetch<Product[]>(`/products/category/${category}`);
107
+ }
108
+
109
+ export async function getCategories(): Promise<string[]> {
110
+ return apiFetch<string[]>('/products/categories');
111
+ }
112
+
113
+ // Carts API
114
+ export async function getAllCarts(): Promise<Cart[]> {
115
+ return apiFetch<Cart[]>('/carts');
116
+ }
117
+
118
+ export async function getCart(id: number): Promise<Cart> {
119
+ return apiFetch<Cart>(`/carts/${id}`);
120
+ }
121
+
122
+ export async function getUserCarts(userId: number): Promise<Cart[]> {
123
+ return apiFetch<Cart[]>(`/carts/user/${userId}`);
124
+ }
125
+
126
+ // Users API
127
+ export async function getAllUsers(): Promise<User[]> {
128
+ return apiFetch<User[]>('/users');
129
+ }
130
+
131
+ export async function getUser(id: number): Promise<User> {
132
+ return apiFetch<User>(`/users/${id}`);
133
+ }
134
+
135
+ // Auth API
136
+ export async function login(
137
+ credentials: LoginCredentials,
138
+ ): Promise<LoginResponse> {
139
+ return apiFetch<LoginResponse>('/auth/login', {
140
+ method: 'POST',
141
+ body: JSON.stringify(credentials),
142
+ });
143
+ }
144
+
145
+ // Export types
146
+ export type {
147
+ Cart,
148
+ CartProduct,
149
+ LoginCredentials,
150
+ LoginResponse,
151
+ Product,
152
+ User,
153
+ };
@@ -0,0 +1,63 @@
1
+ import { Link } from 'reroute-js/react';
2
+ import Header from '../components/Header';
3
+
4
+ export default function NotFoundPage() {
5
+ return (
6
+ <>
7
+ <Header />
8
+
9
+ <main className='container-custom py-16'>
10
+ <div className='max-w-2xl mx-auto text-center'>
11
+ <div className='mb-8 animate-slide-up'>
12
+ <div className='text-9xl font-bold text-primary-600 mb-4'>404</div>
13
+ <h1 className='text-4xl font-bold text-gray-900 mb-4'>
14
+ Page Not Found
15
+ </h1>
16
+ <p className='text-xl text-gray-600 mb-8'>
17
+ Oops! The page you're looking for doesn't exist. It might have
18
+ been moved or deleted.
19
+ </p>
20
+ </div>
21
+
22
+ <div className='flex flex-col sm:flex-row gap-4 justify-center mb-12 animate-in'>
23
+ <Link to='/' className='btn-primary btn-lg'>
24
+ 🏠 Go Home
25
+ </Link>
26
+ <Link to='/products' className='btn-secondary btn-lg'>
27
+ 🛍️ Browse Products
28
+ </Link>
29
+ </div>
30
+
31
+ <div className='grid grid-cols-1 md:grid-cols-3 gap-6 mt-16 animate-scale'>
32
+ <Link
33
+ to='/categories'
34
+ className='card card-hover p-6 no-underline text-gray-900'
35
+ >
36
+ <div className='text-4xl mb-3'>🏷️</div>
37
+ <h3 className='font-semibold mb-2'>Categories</h3>
38
+ <p className='text-sm text-gray-600'>Browse by category</p>
39
+ </Link>
40
+
41
+ <Link
42
+ to='/products'
43
+ className='card card-hover p-6 no-underline text-gray-900'
44
+ >
45
+ <div className='text-4xl mb-3'>📦</div>
46
+ <h3 className='font-semibold mb-2'>All Products</h3>
47
+ <p className='text-sm text-gray-600'>View our catalog</p>
48
+ </Link>
49
+
50
+ <Link
51
+ to='/'
52
+ className='card card-hover p-6 no-underline text-gray-900'
53
+ >
54
+ <div className='text-4xl mb-3'>⚡</div>
55
+ <h3 className='font-semibold mb-2'>Featured</h3>
56
+ <p className='text-sm text-gray-600'>See what's popular</p>
57
+ </Link>
58
+ </div>
59
+ </div>
60
+ </main>
61
+ </>
62
+ );
63
+ }