create-surf-app 0.1.23 → 0.1.25-alpha.0

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.
@@ -31,7 +31,7 @@ Done! Next steps:
31
31
  cd frontend && npm install && cd ..
32
32
 
33
33
  # Start backend
34
- cd backend && PORT=${validatedBackendPort} npm run dev &
34
+ cd backend && npm run dev &
35
35
 
36
36
  # Start frontend
37
37
  cd frontend && npm run dev
@@ -75,7 +75,13 @@ function validatePort(label, value) {
75
75
  function writeEnvFiles(root, frontendPort, backendPort, previewBase) {
76
76
  const backendEnvPath = path.join(root, "backend", ".env");
77
77
  const frontendEnvPath = path.join(root, "frontend", ".env");
78
- fs.writeFileSync(backendEnvPath, `PORT=${backendPort}${os.EOL}`);
78
+ const backendEnv = [
79
+ `PORT=${backendPort}`,
80
+ "SURF_API_KEY=REPLACE_WITH_SURF_API_KEY",
81
+ "# Optional: override the default Surf API host",
82
+ "# SURF_API_BASE_URL=https://api.ask.surf/gateway/v1"
83
+ ].join(os.EOL);
84
+ fs.writeFileSync(backendEnvPath, `${backendEnv}${os.EOL}`);
79
85
  let frontendEnv = `VITE_PORT=${frontendPort}${os.EOL}VITE_BACKEND_PORT=${backendPort}${os.EOL}`;
80
86
  if (previewBase) {
81
87
  frontendEnv += `VITE_BASE=${previewBase}${os.EOL}`;
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createSurfApp
4
- } from "./chunk-MJDS5WZH.js";
4
+ } from "./chunk-GIYEZ736.js";
5
5
 
6
6
  // src/cli.ts
7
7
  var VALUE_FLAGS = /* @__PURE__ */ new Set([
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createSurfApp
3
- } from "./chunk-MJDS5WZH.js";
3
+ } from "./chunk-GIYEZ736.js";
4
4
  export {
5
5
  createSurfApp
6
6
  };
@@ -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
- Everything comes from `@surf-ai/sdk`. Do NOT create local utility files for these.
8
-
9
- **Frontend (`@surf-ai/sdk/react`):**
10
-
11
- ```tsx
12
- import { useMarketPrice, useTokenHolders } from "@surf-ai/sdk/react"; // data hooks
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 `@surf-ai/sdk/react` hooks in frontend, `@surf-ai/sdk/server` dataApi in backend
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.
@@ -2,11 +2,11 @@
2
2
  "name": "backend",
3
3
  "private": true,
4
4
  "scripts": {
5
- "start": "node server.js",
6
- "dev": "node --watch server.js"
5
+ "start": "node --env-file=.env server.js",
6
+ "dev": "node --env-file=.env --watch server.js"
7
7
  },
8
8
  "dependencies": {
9
- "@surf-ai/sdk": "0.1.4-beta",
9
+ "@surf-ai/sdk": "1.0.0-alpha",
10
10
  "express": "4.22.1"
11
11
  }
12
12
  }
@@ -13,7 +13,6 @@
13
13
  "type-check": "tsc --noEmit --incremental"
14
14
  },
15
15
  "dependencies": {
16
- "@surf-ai/sdk": "0.1.4-beta",
17
16
  "@surf-ai/theme": "latest",
18
17
  "react": "19.2.4",
19
18
  "react-dom": "19.2.4",
@@ -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))' }}>
@@ -0,0 +1,3 @@
1
+ export function api(path: string) {
2
+ return `${import.meta.env.BASE_URL}api/${path.replace(/^\/+/, '')}`
3
+ }
@@ -1,7 +1,6 @@
1
1
  /// <reference types="vite/client" />
2
2
 
3
3
  interface ImportMetaEnv {
4
- readonly VITE_API_URL: string
5
4
  readonly VITE_PORT: string
6
5
  readonly VITE_BACKEND_PORT: string
7
6
  }
@@ -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
- // Use loadEnv to read .env before config evaluation.
7
- // Vite does NOT load .env files until after the config is resolved,
8
- // so process.env.VITE_* is unavailable here without this.
9
- const env = loadEnv('development', process.cwd())
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
- const BACKEND_PORT = readRequiredPort('VITE_BACKEND_PORT')
21
- const BASE = env.VITE_BASE || './'
22
-
23
- // Vite proxy middleware runs BEFORE base-path stripping. When base is
24
- // non-relative (e.g. /preview/staging/{sid}/), proxy keys must be prefixed
25
- // with base to match incoming requests.
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
- target: `http://127.0.0.1:${BACKEND_PORT}`,
33
- changeOrigin: true,
34
- // Strip base prefix so Express receives clean paths
35
- ...(hasAbsBase && {
36
- rewrite: (path: string) => path.replace(BASE, '/'),
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
- export default defineConfig({
41
- plugins: [react(), tailwindcss()],
42
- server: {
43
- port: readRequiredPort('VITE_PORT'),
44
- host: '0.0.0.0',
45
- proxy: {
46
- // /proxy/* → Express → OutboundProxy (crypto data APIs)
47
- [proxyKey]: backendProxy,
48
- // /api/* Express (custom backend routes: auth, CRUD, etc.)
49
- [apiProxyKey]: backendProxy,
50
- },
51
- hmr: {
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
- // Pre-transform entry files at startup so deps are optimized before the
60
- // first browser request. Reduces the window where Vite serves dep stubs
61
- // (partially-optimized modules) during cold start after session respawn.
62
- warmup: {
63
- clientFiles: ['./src/entry-client.tsx', './src/App.tsx'],
46
+ resolve: {
47
+ alias: {
48
+ '@': path.resolve(__dirname, './src'),
49
+ },
50
+ dedupe: ['react', 'react-dom'],
51
+ preserveSymlinks: true,
64
52
  },
65
- },
66
- // Force Vite to always resolve a single copy of React.
67
- // Without this, dep re-optimization after `npm install <new-pkg>` can create
68
- // a new React bundle (different hash) while dynamic imports still reference
69
- // the old one → two React instances → "Invalid hook call" errors.
70
- resolve: {
71
- alias: {
72
- "@": path.resolve(__dirname, "./src"),
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
- dedupe: ['react', 'react-dom'],
75
- // CRITICAL: node_modules is a tmpfs mount for performance.
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
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-surf-app",
3
- "version": "0.1.23",
3
+ "version": "0.1.25-alpha.0",
4
4
  "description": "Scaffold a Surf app — Vite + React + Express + @surf-ai/sdk",
5
5
  "type": "module",
6
6
  "bin": {