create-surf-app 0.1.22 → 0.1.24
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.
- package/dist/templates/default/CLAUDE.md +12 -10
- package/dist/templates/default/backend/server.js +1 -1
- package/dist/templates/default/frontend/package.json +1 -1
- package/dist/templates/default/frontend/src/App.tsx +1 -0
- package/dist/templates/default/frontend/src/lib/api.ts +3 -0
- package/dist/templates/default/frontend/src/vite-env.d.ts +0 -1
- package/dist/templates/default/frontend/vite.config.ts +49 -143
- package/package.json +1 -1
|
@@ -4,13 +4,12 @@ Built with [Surf SDK](https://github.com/cyberconnecthq/urania/tree/main/package
|
|
|
4
4
|
|
|
5
5
|
## Imports from @surf-ai/sdk
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
```
|
|
7
|
+
The frontend should call your own backend routes under the Vite base path.
|
|
8
|
+
Reuse the scaffolded `frontend/src/lib/api.ts` helper for frontend API requests.
|
|
9
|
+
Call routes like `fetch(api('wallet'))`.
|
|
10
|
+
Do not use absolute `/api/...` fetch URLs in the frontend.
|
|
11
|
+
Do not use Surf SDK React hooks in the frontend.
|
|
12
|
+
Use `@surf-ai/sdk/server` in backend routes to talk to Surf data APIs.
|
|
14
13
|
|
|
15
14
|
**Backend (`@surf-ai/sdk/server`):**
|
|
16
15
|
|
|
@@ -30,6 +29,7 @@ const raw = await dataApi.get("newcategory/endpoint", { foo: "bar" });
|
|
|
30
29
|
```
|
|
31
30
|
frontend/src/App.tsx - build your UI here
|
|
32
31
|
frontend/src/components/ - add components
|
|
32
|
+
frontend/src/lib/api.ts - base-aware helper for frontend API calls
|
|
33
33
|
backend/routes/*.js - add API routes (auto-mounted at /api/{name})
|
|
34
34
|
backend/db/schema.js - define database tables
|
|
35
35
|
```
|
|
@@ -47,7 +47,6 @@ backend/db/schema.js - define database tables
|
|
|
47
47
|
| `/api/cron/:id` | PATCH | Update a cron task (schedule, enabled, etc.) |
|
|
48
48
|
| `/api/cron/:id` | DELETE | Delete a cron task |
|
|
49
49
|
| `/api/cron/:id/run` | POST | Manually trigger a cron task |
|
|
50
|
-
| `/proxy/*` | ANY | Data API passthrough - `/proxy/market/price` -> hermod |
|
|
51
50
|
|
|
52
51
|
Auto-registered from `backend/routes/*.js`:
|
|
53
52
|
| File | Endpoint |
|
|
@@ -73,7 +72,7 @@ The agent can also call `POST /api/__sync-schema` explicitly after editing.
|
|
|
73
72
|
|
|
74
73
|
## Do NOT modify
|
|
75
74
|
|
|
76
|
-
- `vite.config.ts` - proxy and build config
|
|
75
|
+
- `vite.config.ts` - API proxy and build config
|
|
77
76
|
- `backend/server.js` - uses @surf-ai/sdk/server
|
|
78
77
|
- `entry-client.tsx` - app bootstrap with SSR hydration
|
|
79
78
|
- `entry-server.tsx` - SSR render for deploy
|
|
@@ -83,6 +82,9 @@ The agent can also call `POST /api/__sync-schema` explicitly after editing.
|
|
|
83
82
|
|
|
84
83
|
## Rules
|
|
85
84
|
|
|
86
|
-
- Use
|
|
85
|
+
- Use the scaffolded `api(path)` helper from `frontend/src/lib/api.ts` for frontend API calls
|
|
86
|
+
- Never use absolute `/api/...` URLs in frontend fetch calls
|
|
87
|
+
- Use `@surf-ai/sdk/server` `dataApi` in backend code when you need Surf data
|
|
88
|
+
- Do not bypass your backend routes from the frontend
|
|
87
89
|
- Frontend packages are pre-installed - check `package.json` before installing
|
|
88
90
|
- Default to a dark theme unless the user explicitly asks for a different visual direction.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
const { createServer } = require('@surf-ai/sdk/server')
|
|
2
|
-
createServer().start()
|
|
2
|
+
createServer({ proxy: false }).start()
|
|
@@ -8,6 +8,7 @@ function SurfLogo() {
|
|
|
8
8
|
)
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
// Frontend API calls should use `src/lib/api.ts`, not absolute `/api/...` URLs.
|
|
11
12
|
export default function App() {
|
|
12
13
|
return (
|
|
13
14
|
<div data-surf-placeholder className="min-h-screen flex flex-col items-center justify-center gap-4 rounded-[14px]" style={{ background: 'var(--bg-chat-nav, #f4f4f4)', border: '1px solid var(--border-strong, rgba(42,42,42,0.08))' }}>
|
|
@@ -3,12 +3,10 @@ import { defineConfig, loadEnv } from 'vite'
|
|
|
3
3
|
import react from '@vitejs/plugin-react'
|
|
4
4
|
import tailwindcss from '@tailwindcss/vite'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
function readRequiredPort(name: 'VITE_BACKEND_PORT' | 'VITE_PORT') {
|
|
6
|
+
function readRequiredPort(
|
|
7
|
+
env: Record<string, string>,
|
|
8
|
+
name: 'VITE_BACKEND_PORT' | 'VITE_PORT',
|
|
9
|
+
) {
|
|
12
10
|
const value = env[name]
|
|
13
11
|
const port = Number.parseInt(value || '', 10)
|
|
14
12
|
if (!Number.isInteger(port)) {
|
|
@@ -17,146 +15,54 @@ function readRequiredPort(name: 'VITE_BACKEND_PORT' | 'VITE_PORT') {
|
|
|
17
15
|
return port
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// See: https://vite.dev/config/server-options#server-proxy
|
|
27
|
-
const hasAbsBase = BASE.startsWith('/')
|
|
28
|
-
const proxyKey = hasAbsBase ? `${BASE}proxy` : '/proxy'
|
|
29
|
-
const apiProxyKey = hasAbsBase ? `${BASE}api` : '/api'
|
|
18
|
+
export default defineConfig(({ mode }) => {
|
|
19
|
+
const env = loadEnv(mode, process.cwd())
|
|
20
|
+
const backendPort = readRequiredPort(env, 'VITE_BACKEND_PORT')
|
|
21
|
+
const base = env.VITE_BASE || './'
|
|
22
|
+
const hasAbsBase = base.startsWith('/')
|
|
23
|
+
const apiBasePrefix = hasAbsBase ? base.replace(/\/$/, '') : ''
|
|
30
24
|
|
|
31
|
-
const backendProxy = {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
}
|
|
25
|
+
const backendProxy = {
|
|
26
|
+
target: `http://127.0.0.1:${backendPort}`,
|
|
27
|
+
changeOrigin: true,
|
|
28
|
+
...(hasAbsBase && {
|
|
29
|
+
rewrite: (requestPath: string) => requestPath.replace(base, '/'),
|
|
30
|
+
}),
|
|
31
|
+
}
|
|
39
32
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// Relative path so Vite prepends the base (VITE_BASE) to construct
|
|
53
|
-
// the full WebSocket URL: wss://host/{base}/ws/vite-hmr
|
|
54
|
-
// This ensures HMR goes through the same preview proxy chain
|
|
55
|
-
// (frontend rewrite → Muninn → Urania → Vite dev server).
|
|
56
|
-
// An absolute path (starting with /) would bypass the proxy entirely.
|
|
57
|
-
path: 'ws/vite-hmr',
|
|
33
|
+
return {
|
|
34
|
+
plugins: [react(), tailwindcss()],
|
|
35
|
+
server: {
|
|
36
|
+
port: readRequiredPort(env, 'VITE_PORT'),
|
|
37
|
+
host: '0.0.0.0',
|
|
38
|
+
proxy: {
|
|
39
|
+
[`${apiBasePrefix}/api`]: backendProxy,
|
|
40
|
+
},
|
|
41
|
+
// Keep the HMR socket under the preview base path.
|
|
42
|
+
hmr: {
|
|
43
|
+
path: 'ws/vite-hmr',
|
|
44
|
+
},
|
|
58
45
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
46
|
+
resolve: {
|
|
47
|
+
alias: {
|
|
48
|
+
'@': path.resolve(__dirname, './src'),
|
|
49
|
+
},
|
|
50
|
+
dedupe: ['react', 'react-dom'],
|
|
51
|
+
preserveSymlinks: true,
|
|
64
52
|
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
53
|
+
// Pre-bundle the deps touched during the initial boot path so cold starts
|
|
54
|
+
// do not race Vite's lazy dependency optimizer.
|
|
55
|
+
optimizeDeps: {
|
|
56
|
+
include: [
|
|
57
|
+
'react',
|
|
58
|
+
'react-dom',
|
|
59
|
+
'react-dom/client',
|
|
60
|
+
'react/jsx-dev-runtime',
|
|
61
|
+
'react/jsx-runtime',
|
|
62
|
+
'@tanstack/react-query',
|
|
63
|
+
'@tanstack/query-core',
|
|
64
|
+
],
|
|
73
65
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// With preserveSymlinks=false (default), esbuild and Vite may resolve
|
|
77
|
-
// paths through the tmpfs layer differently after npm install modifies
|
|
78
|
-
// the directory — old dep bundles reference the previous real path
|
|
79
|
-
// → two React instances → "Cannot read properties of null
|
|
80
|
-
// (reading 'useState')".
|
|
81
|
-
// With preserveSymlinks=true, module identity uses the logical path
|
|
82
|
-
// (frontend/node_modules/react) which stays constant regardless of
|
|
83
|
-
// underlying mount changes → single React instance always.
|
|
84
|
-
preserveSymlinks: true,
|
|
85
|
-
},
|
|
86
|
-
// Eagerly pre-bundle ALL scaffold deps at startup.
|
|
87
|
-
// Without this, Vite discovers deps lazily by crawling imports. When the agent
|
|
88
|
-
// rewrites code between turns, new imports trigger dep re-optimization which
|
|
89
|
-
// serves "stub" modules (all exports undefined) → "Cannot read properties of
|
|
90
|
-
// null (reading 'useEffect')" and "Loading dependencies..." reload loops.
|
|
91
|
-
// Pre-bundling everything eliminates lazy discovery entirely.
|
|
92
|
-
optimizeDeps: {
|
|
93
|
-
include: [
|
|
94
|
-
// React core
|
|
95
|
-
'react',
|
|
96
|
-
'react-dom',
|
|
97
|
-
'react-dom/client',
|
|
98
|
-
'react/jsx-dev-runtime',
|
|
99
|
-
'react/jsx-runtime',
|
|
100
|
-
// Data fetching
|
|
101
|
-
'@tanstack/react-query',
|
|
102
|
-
'@tanstack/query-core',
|
|
103
|
-
// Charts
|
|
104
|
-
'echarts',
|
|
105
|
-
'echarts-for-react',
|
|
106
|
-
'echarts/core',
|
|
107
|
-
'echarts/charts',
|
|
108
|
-
'echarts/components',
|
|
109
|
-
'echarts/renderers',
|
|
110
|
-
// UI primitives (Radix)
|
|
111
|
-
'@radix-ui/react-accordion',
|
|
112
|
-
'@radix-ui/react-aspect-ratio',
|
|
113
|
-
'@radix-ui/react-avatar',
|
|
114
|
-
'@radix-ui/react-checkbox',
|
|
115
|
-
'@radix-ui/react-collapsible',
|
|
116
|
-
'@radix-ui/react-context-menu',
|
|
117
|
-
'@radix-ui/react-dialog',
|
|
118
|
-
'@radix-ui/react-dropdown-menu',
|
|
119
|
-
'@radix-ui/react-hover-card',
|
|
120
|
-
'@radix-ui/react-label',
|
|
121
|
-
'@radix-ui/react-menubar',
|
|
122
|
-
'@radix-ui/react-navigation-menu',
|
|
123
|
-
'@radix-ui/react-popover',
|
|
124
|
-
'@radix-ui/react-progress',
|
|
125
|
-
'@radix-ui/react-radio-group',
|
|
126
|
-
'@radix-ui/react-scroll-area',
|
|
127
|
-
'@radix-ui/react-select',
|
|
128
|
-
'@radix-ui/react-separator',
|
|
129
|
-
'@radix-ui/react-slider',
|
|
130
|
-
'@radix-ui/react-slot',
|
|
131
|
-
'@radix-ui/react-switch',
|
|
132
|
-
'@radix-ui/react-tabs',
|
|
133
|
-
'@radix-ui/react-toast',
|
|
134
|
-
'@radix-ui/react-toggle',
|
|
135
|
-
'@radix-ui/react-toggle-group',
|
|
136
|
-
'@radix-ui/react-tooltip',
|
|
137
|
-
// UI components
|
|
138
|
-
'sonner',
|
|
139
|
-
'cmdk',
|
|
140
|
-
'vaul',
|
|
141
|
-
'embla-carousel-react',
|
|
142
|
-
'react-day-picker',
|
|
143
|
-
'react-resizable-panels',
|
|
144
|
-
// Forms & validation
|
|
145
|
-
'react-hook-form',
|
|
146
|
-
'@hookform/resolvers',
|
|
147
|
-
'zod',
|
|
148
|
-
// Utilities
|
|
149
|
-
'lucide-react',
|
|
150
|
-
'next-themes',
|
|
151
|
-
'class-variance-authority',
|
|
152
|
-
'clsx',
|
|
153
|
-
'tailwind-merge',
|
|
154
|
-
'date-fns',
|
|
155
|
-
'scheduler',
|
|
156
|
-
],
|
|
157
|
-
},
|
|
158
|
-
// Base path set dynamically to match external preview URL.
|
|
159
|
-
// This ensures Vite's internal modules (/@vite/client, /@react-refresh, etc.)
|
|
160
|
-
// are served under the correct path through the multi-layer proxy chain.
|
|
161
|
-
base: BASE,
|
|
66
|
+
base,
|
|
67
|
+
}
|
|
162
68
|
})
|