spfn 0.2.0-beta.6 → 0.2.0-beta.60

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 INFLIKE Inc.
3
+ Copyright (c) 2025 FXY Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,260 +1,420 @@
1
- # spfn
1
+ # spfn — the SPFN CLI (backend layer for Next.js)
2
2
 
3
- > Superfunction CLI - The Backend Layer for Next.js
3
+ `spfn` scaffolds and runs a Hono-based backend that lives inside a Next.js project.
4
+ It creates the server structure, runs the dev/build/start lifecycle, manages the
5
+ database (Drizzle Kit), generates the RPC route map, and validates environment variables.
4
6
 
5
- The official CLI tool for SPFN framework. Initialize projects, generate boilerplate code, and manage your database.
7
+ > Beta: install with the `@beta` tag (`spfn@beta`). The binary is `spfn`.
6
8
 
7
- ## Usage
9
+ ## Install
8
10
 
9
- > ⚠️ **Alpha Release**: SPFN is currently in alpha. Use `@alpha` tag for installation.
11
+ No global install needed run through your package manager's dlx/npx:
10
12
 
11
- ### Quick Start (New Project)
12
13
  ```bash
13
- # Create new project with all SPFN features pre-configured
14
- npx spfn@alpha create my-app
15
- cd my-app
16
- docker compose up -d
17
- npm run spfn:dev
14
+ npx spfn@beta <command>
15
+ pnpm dlx spfn@beta <command>
18
16
  ```
19
17
 
20
- ### Add to Existing Next.js Project
18
+ Or add it as a project dependency (`spfn init`/`spfn create` do this for you), then
19
+ call it via `pnpm spfn <command>` / `npm run spfn:<script>`.
20
+
21
+ Requirements: Node.js 18.18+, Next.js 15+ (App Router, `src/` dir), PostgreSQL (Redis optional).
22
+
23
+ ## Usage
24
+
21
25
  ```bash
22
- # Using npx (no installation required) - Recommended
23
- npx spfn@alpha init
26
+ # New project (runs create-next-app + spfn init)
27
+ npx spfn@beta create my-app
28
+ cd my-app
29
+ docker compose up -d # Postgres + Redis
30
+ # .env.local & .env.server are generated — put server secrets in .env.server
31
+ pnpm spfn:dev # Next.js :3790 + SPFN API :8790
24
32
 
25
- # Or install globally (alpha version)
26
- npm install -g spfn@alpha
27
- spfn init
33
+ # Add SPFN to an existing Next.js project
34
+ npx spfn@beta init
28
35
  ```
29
36
 
37
+ The package manager is auto-detected (pnpm > yarn > bun > npm) from lockfiles; override
38
+ with `--pm`. In a pnpm workspace, `create` installs from the workspace root.
39
+
40
+ ---
41
+
30
42
  ## Commands
31
43
 
32
- ### Create New Project
33
- ```bash
34
- spfn create <name> # Create new Next.js project with SPFN (all-in-one)
35
- spfn create my-app # Example: Create project with TypeScript, App Router, SVGR, and SPFN
36
- spfn create my-app --shadcn # Include shadcn/ui component library
37
- ```
44
+ Registered top-level commands: `create`, `init`, `add`, `dev`, `build`, `start`,
45
+ `codegen`, `db`, `env`, `key`, `setup`.
38
46
 
39
- ### Project Initialization
40
- ```bash
41
- spfn init # Initialize SPFN in existing Next.js project
42
- spfn init -y # Skip prompts, use defaults
43
- ```
47
+ ### `spfn create <name>`
44
48
 
45
- **What `spfn init` creates:**
46
- - `src/lib/contracts/` - API contracts (shared between frontend and backend)
47
- - `src/server/routes/` - Backend route handlers
48
- - `src/server/entities/` - Database entities (Drizzle ORM)
49
- - `docker-compose.yml` - PostgreSQL + Redis for local development
50
- - `Dockerfile`, `.dockerignore`, `docker-compose.production.yml` - Production deployment
51
- - `.env.local.example` - Environment variable template
52
- - `spfn.config.js` - Deployment configuration with JSDoc type hints
53
- - `.spfnrc.json` - Code generation configuration
54
-
55
- **SPFN's Contract-Based Architecture:**
56
- - **Contracts** (`src/lib/contracts/`): Define API endpoints with absolute paths (e.g., `/users/:id`)
57
- - **Handlers** (`src/server/routes/`): Import contracts and implement business logic
58
- - **Frontend**: Import contracts for type-safe API calls
59
- - **Auto-generated Client**: `src/lib/api/` is auto-generated from contracts (via `spfn dev` or `spfn build`)
60
-
61
- ### Generate Function Modules
62
- ```bash
63
- spfn generate fn <name> # Generate new SPFN function module (interactive)
64
- spfn g fn <name> # Short alias
49
+ Runs `create-next-app` with SPFN-recommended flags (TypeScript, App Router, `src/`,
50
+ Tailwind, import alias `@/*`, no ESLint), sets up SVGR icons, then runs `init`.
65
51
 
66
- # With options
67
- spfn g fn blog -e posts,comments -y # Create blog module with posts & comments entities
68
- spfn g fn shop -d "E-commerce shop" -e products,orders,customers
69
- ```
52
+ | Option | Description |
53
+ |--------|-------------|
54
+ | `--pm <manager>` | Force package manager: `npm` \| `pnpm` \| `yarn` \| `bun` |
55
+ | `--shadcn` | Also run `shadcn init` |
56
+ | `--skip-install` | Skip dependency install |
57
+ | `--skip-git` | Skip `git init` |
58
+ | `-y, --yes` | Skip prompts, use defaults |
70
59
 
71
- **What `spfn generate fn` creates:**
72
- - `packages/<name>/` - New function module in monorepo
73
- - `src/server/entities/schema.ts` - **Exported schema** (ensures CREATE SCHEMA in migrations)
74
- - `src/server/entities/*.ts` - Drizzle ORM entity definitions (import schema)
75
- - `src/server/repositories/` - CRUD repository layer
76
- - `src/server/routes/` - RESTful API route handlers
77
- - `src/lib/contracts/` - TypeBox API contracts
78
- - `package.json` - Pre-configured with SPFN metadata
79
- - `tsup.config.ts` - Build configuration
80
- - `drizzle.config.ts` - Database migration setup
81
-
82
- **Database Schema Naming:**
83
- - Scope and module name are automatically converted to safe PostgreSQL schema names
84
- - Examples: `@my-company/blog` → `my_company_blog`, `@spfn/cms` → `spfn_cms`
85
- - Special characters (`.`, `!`, `-`) are converted to underscores
86
- - Names starting with numbers get `_` prefix (e.g., `@123company` → `_123company`)
87
- - Ensures PostgreSQL compatibility (lowercase, numbers, underscores only)
88
-
89
- **Options:**
90
- - `-e, --entities <list>` - Comma-separated entity names
91
- - `-d, --description <text>` - Module description
92
- - `--skip-routes` - Generate entities without routes
93
- - `--skip-cache` - Skip cache generation
94
- - `-y, --yes` - Skip all prompts
95
-
96
- **Example workflow:**
97
- ```bash
98
- # 1. Generate module
99
- spfn g fn blog -e posts,comments -y
60
+ ### `spfn init`
100
61
 
101
- # 2. Build the module
102
- cd packages/blog
103
- npm run build
62
+ Adds SPFN to an existing Next.js project: copies the server templates, wires the RPC
63
+ proxy route, Docker files, deploy + codegen config, updates `package.json` scripts/deps,
64
+ and installs. With auth enabled it also adds the `/_auth/:path*` → SPFN API rewrite to
65
+ `next.config` (OAuth callbacks return to the app origin; merged manually if a `rewrites()`
66
+ already exists). See [Scaffold structure](#scaffold-structure) for what lands on disk.
104
67
 
105
- # 3. Install in your app
106
- spfn add @spfn/blog
107
- ```
68
+ | Option | Description |
69
+ |--------|-------------|
70
+ | `-y, --yes` | Skip prompts, use defaults |
71
+
72
+ Generated projects pin `drizzle-orm` and `drizzle-kit` to `1.0.0-rc.4`, matching
73
+ `@spfn/core` and the rest of the published SPFN database packages.
74
+
75
+ ### `spfn add <package>`
76
+
77
+ Installs an SPFN ecosystem package and applies its pre-built migrations. The package
78
+ name must be scoped (contain `/`).
108
79
 
109
- ### Install Ecosystem Packages
110
80
  ```bash
111
- spfn add <package> # Install SPFN ecosystem package with automatic DB setup
112
- spfn add @spfn/cms # Install CMS package
113
- spfn add @company/plugin # Install third-party SPFN package
81
+ pnpm spfn add @spfn/cms
82
+ pnpm spfn add @mycompany/spfn-analytics
114
83
  ```
115
84
 
116
- **What `spfn add` does:**
117
- 1. Installs the package via pnpm/npm
118
- 2. Discovers package's pre-built migrations
119
- 3. Applies package migrations to your database
120
- 4. ✅ Shows package-specific setup guide
85
+ How it works: if not already present it installs the package, then reads the package's
86
+ `spfn` field in its `package.json` (`migrations`, `setupMessage`) and applies any
87
+ function migrations to `DATABASE_URL`. If `DATABASE_URL` is unset, migration is skipped
88
+ with a hint to run `spfn db push` later. Works with published and workspace packages.
121
89
 
122
- **Example: Installing @spfn/cms**
123
- ```bash
124
- $ pnpm spfn add @spfn/cms
90
+ ### `spfn dev`
91
+
92
+ Starts the SPFN server + Next.js (and a codegen watcher). The server must report ready
93
+ (via a `.spfn/server-ready` signal file) before Next.js launches. Runs through `tsx`,
94
+ no pre-build needed.
95
+
96
+ | Option | Description | Default |
97
+ |--------|-------------|---------|
98
+ | `--server-only` | Run only the SPFN/Hono server (also auto-selected if Next.js isn't a dependency) | off |
99
+ | `--watch` | Restart the server on `src/server` changes (chokidar) | off |
100
+ | `-p, --port <port>` | Server port | from `server.config.ts` / env (`4000` in server-only fallback) |
101
+ | `-H, --host <host>` | Server host | `localhost` |
102
+ | `--routes <path>` | Routes directory path | server default |
103
+
104
+ Note: hot reload is **off by default** — pass `--watch` to restart on file changes.
105
+
106
+ ### `spfn build`
107
+
108
+ Runs codegen, builds Next.js (via the project's `build` script), and compiles
109
+ `src/server/**/*.ts` → `.spfn/server` with tsup. Also writes `.spfn/prod-server.mjs`
110
+ (the production entry consumed by `spfn start`).
111
+
112
+ | Option | Description |
113
+ |--------|-------------|
114
+ | `--server-only` | Build only the SPFN server (skip Next.js) |
115
+ | `--next-only` | Build only Next.js (skip the SPFN server) |
116
+ | `--turbo` | Use Turbopack for the Next.js build |
117
+
118
+ ### `spfn start`
119
+
120
+ Starts the production servers from build output. Requires `spfn build` first — it errors
121
+ if `.spfn/server`, `.spfn/prod-server.mjs`, or `.next` are missing.
122
+
123
+ | Option | Description | Default |
124
+ |--------|-------------|---------|
125
+ | `--server-only` | Run only the SPFN server | off |
126
+ | `--next-only` | Run only Next.js | off |
127
+ | `-p, --port <port>` | SPFN server port (sets `SPFN_PORT`) | `8790` |
128
+ | `-h, --host <host>` | SPFN server host (sets `SPFN_HOST`) | `0.0.0.0` |
125
129
 
126
- 📦 Setting up @spfn/cms...
127
- ✓ Package installed
130
+ Next.js is started on `0.0.0.0:3790`. Both run together via `concurrently --kill-others`.
128
131
 
129
- 🗄️ Setting up database for @spfn/cms...
130
- ✓ Migrations applied
132
+ ### `spfn codegen`
131
133
 
132
- @spfn/cms installed successfully!
134
+ Manages code generators driven by `.spfnrc.ts`. The default generator is
135
+ `@spfn/core:route-map`, which emits `src/generated/route-map.ts` from `src/server/router.ts`
136
+ so the RPC proxy can resolve routes without importing server code. Generators also run
137
+ automatically during `spfn dev` and `spfn build`.
133
138
 
134
- 📚 Setup Guide:
135
- 1. Import CMS components: import { useLabels } from '@spfn/cms'
136
- 2. View labels in Drizzle Studio: pnpm spfn db studio
137
- 3. Learn more: https://github.com/spfnio/spfn
139
+ | Subcommand | Description |
140
+ |------------|-------------|
141
+ | `codegen init` | Create `.spfnrc.ts` (`--with-example` shows custom-generator usage) |
142
+ | `codegen list` (`ls`) | List configured generators and their watch patterns |
143
+ | `codegen run` | Run all generators once (no watch) |
144
+
145
+ `package.json` exposes this as the `codegen` script (`spfn codegen run`).
146
+
147
+ To add a custom generator, implement the `Generator` interface from `@spfn/core/codegen`
148
+ and reference it in `.spfnrc.ts`:
149
+
150
+ ```ts
151
+ // .spfnrc.ts
152
+ import { defineConfig, defineGenerator } from '@spfn/core/codegen';
153
+
154
+ export default defineConfig({
155
+ generators: [
156
+ defineGenerator({
157
+ name: '@spfn/core:route-map',
158
+ routerPath: './src/server/router.ts',
159
+ outputPath: './src/generated/route-map.ts',
160
+ }),
161
+ ],
162
+ });
138
163
  ```
139
164
 
140
- **How it works:**
141
- - SPFN automatically discovers schemas from packages via `package.json`:
142
- ```json
143
- {
144
- "name": "@spfn/cms",
145
- "spfn": {
146
- "schemas": ["./dist/server/entities/*.js"],
147
- "migrations": { "dir": "./migrations" },
148
- "setupMessage": "📚 Next steps: ..."
149
- }
150
- }
151
- ```
152
- - Packages include pre-built migrations in their `migrations/` directory
153
- - Package migrations are applied first, then project migrations
154
- - Works with both published npm packages and local development (workspace packages)
155
-
156
- ### Development & Production
165
+ ### `spfn db`
166
+
167
+ Wraps Drizzle Kit with auto-generated config. Most commands read `DATABASE_URL` from the
168
+ loaded `.env` chain.
169
+
170
+ | Subcommand | Description |
171
+ |------------|-------------|
172
+ | `db generate` (`g`) | Generate migrations from schema changes (timestamp-prefixed) |
173
+ | `db push` | Diff with Drizzle Kit's current PostgreSQL engine and apply the selected DDL atomically. Destructive changes need confirmation; `--force` applies them, `--dry-run` previews |
174
+ | `db migrate` (`m`) | Run pending migrations. `--with-backup` snapshots first |
175
+ | `db studio` | Open Drizzle Studio. `-p, --port` (auto-finds a free port) |
176
+ | `db check` | Verify the database connection |
177
+ | `db drop` | Drop all tables **destructive**, double-prompts (see [Pitfalls](#pitfalls)) |
178
+ | `db backup` | Create a backup (`-f sql|custom`, `-o`, `-s`, `--data-only`, `--schema-only`, `--tag`, `--env`) |
179
+ | `db restore [file]` | Restore from a backup (`--drop`, `-s`, `--data-only`, `--schema-only`, `-v`) |
180
+ | `db backup:list` | List backups |
181
+ | `db backup:clean` | Prune backups (`-k, --keep <n>`, `-o, --older-than <days>`) |
182
+ | `db reindex` | Convert sequential migration prefixes to timestamps (`--dry-run`) |
183
+
184
+ > `db push` is for development. For production, use `db generate` + `db migrate` to keep
185
+ > migration history.
186
+
187
+ `db push` and `db migrate` also replay migrations shipped by installed SPFN function
188
+ packages (`@spfn/auth`, `@spfn/cms`, …) into per-package tracking tables
189
+ (`drizzle.__spfn_fn_<pkg>_migrations`). The CLI applies these with a built-in runner
190
+ that reads both migration layouts — drizzle-kit ≤0.31 (`NNNN_name.sql` +
191
+ `meta/_journal.json`) and drizzle-kit 1.0 (`<timestamp>_name/migration.sql`) — so a
192
+ package's layout never has to match the CLI's bundled drizzle version. `db push`
193
+ validates every package's migration folder before applying the project schema, and a
194
+ function-migration failure after a successful schema apply exits 1 with a message
195
+ making clear the project schema was already committed.
196
+
197
+ Database TLS is controlled by `DATABASE_URL`. Loopback URLs (`localhost`, `127.0.0.1`,
198
+ and `::1`) default to `ssl: false`; add an explicit `sslmode` when the local server uses
199
+ TLS. For a TLS connection with a self-signed certificate, set
200
+ `SPFN_DB_INSECURE_TLS=1` to disable certificate verification. This opt-in never enables
201
+ TLS by itself and `sslmode=disable` remains authoritative.
202
+
203
+ ### `spfn env`
204
+
205
+ Schema-driven environment variable tooling (schema comes from a package's `envSchema`,
206
+ default `@spfn/core`). Routes vars to the right file: `NEXT_PUBLIC_*` → `.env`/`.env.local`,
207
+ server vars → `.env.server`.
208
+
209
+ | Subcommand | Description |
210
+ |------------|-------------|
211
+ | `env list` | List vars from the schema (`-g` groups by target file) |
212
+ | `env stats` | Show variable statistics |
213
+ | `env search <query>` | Search vars by key or description |
214
+ | `env init` | Generate `.env` template files (`-e <env>` for per-env, `-f` to overwrite) |
215
+ | `env check` | Check `.env` files against the schema (`-e <env>` for a full env chain) |
216
+ | `env validate` | Validate `process.env` against the schema — for CI/CD (`-e <env>`, `-s` strict) |
217
+
218
+ All accept `-p, --package <pkg>` (`env validate` uses `-p, --packages <pkgs...>`).
219
+
220
+ ### `spfn key [preset]`
221
+
222
+ Generate cryptographically random secrets (base64url, 256-bit default).
223
+
157
224
  ```bash
158
- # Development
159
- spfn dev # Start both Next.js (3790) + API server (8790)
160
- spfn dev --server-only # Start API server only (8790)
161
- spfn dev --no-watch # Disable hot reload
162
-
163
- # Production
164
- spfn build # Build Next.js + compile server
165
- spfn start # Start production server
166
- spfn start --server-only # Start API server only (no Next.js)
225
+ spfn key # generic 256-bit secret
226
+ spfn key auth-encryption -c # preset key, copy to clipboard
227
+ spfn key --list # list presets
228
+ spfn key gen -b 64 # raw value only, no metadata (alias of `key generate`)
167
229
  ```
168
230
 
169
- ### Database Management
170
- ```bash
171
- spfn db generate # Generate database migrations
172
- spfn db push # Push schema to database (no migrations)
173
- spfn db migrate # Run pending migrations
174
- spfn db studio # Open Drizzle Studio (database GUI)
175
- spfn db check # Check database connection
176
- spfn db drop # Drop all tables (⚠️ dangerous!)
231
+ Presets: `auth-encryption`, `nextauth-secret`, `jwt-secret`, `session-secret`, `api-key`.
232
+ Options: `-l, --list`, `-b, --bytes <n>` (1–128), `-e, --env <name>`, `-c, --copy`.
233
+ The command prints the value to stdout for you to paste into an env file — it does **not**
234
+ write any file.
235
+
236
+ ### `spfn secret`
237
+
238
+ Unified secret management: local secrets live in the OS keychain, deployed secrets in
239
+ encrypted SOPS files. The runtime never sees a reference — `spfn dev` injects local
240
+ values into the server process and GitOps injects them in production, so the app always
241
+ reads plain `process.env`.
242
+
243
+ | Subcommand | Description |
244
+ |------------|-------------|
245
+ | `secret set [key]` | Store a value (masked prompt). `--env local` → keychain; other envs → SOPS |
246
+ | `secret list` | List declared secrets and their status per env (never prints values) |
247
+ | `secret generate [key]` | Mint values for schema secrets with a `generate` strategy (`-a/--all`) |
248
+ | `secret rotate [key]` | Rotate values; external secrets are flagged for manual reissue (`-a/--all`) |
249
+ | `secret keygen` | Generate an age key pair for the SOPS no-cloud backend |
250
+ | `secret recipients <add\|remove\|list> [age1…]` | Manage `.sops.yaml` recipients + re-encrypt |
251
+ | `secret check` | Static lint — flag plaintext secret leaks |
252
+
253
+ Options: `-e, --env <env>` (`local` default; also `development`/`staging`/`production`),
254
+ `-p, --package <pkg>` (schema source, default `@spfn/core`).
255
+
256
+ **Local (keychain).** `spfn secret set DB_URL` stores the value in the OS keychain
257
+ (macOS `security`, Windows Credential Manager via optional `@napi-rs/keyring`, Linux
258
+ libsecret) and writes a `secret:keychain:spfn_DB_URL` reference into `.env.server`. The
259
+ reference is not sensitive; the real value never lands in the repo. `spfn dev` resolves
260
+ and injects it. Note: injection happens only when the server is started via `spfn dev` —
261
+ running the app another way (a bare `node`, tests) would see the raw reference, so use
262
+ `spfn dev` locally (a runtime resolver for other runners is planned).
263
+
264
+ **Deployed (SOPS).** `spfn secret set DB_URL --env production` writes the value into
265
+ `secrets/production.enc.json`, encrypted by SOPS. The backend (age / GCP KMS / AWS KMS)
266
+ is chosen by `.sops.yaml` creation rules — KMS needs no local key file (IAM + cloud
267
+ auth), age is the no-cloud fallback (`secret keygen` + `secret recipients add`). Commit
268
+ the encrypted file; your GitOps step decrypts it into env at deploy time. `sops`/`age`
269
+ are needed only for the deployed envs, never for local keychain use.
270
+
271
+ Schema-driven: a secret declared with `envSecret({ generate: 'base64url32' })` can be
272
+ minted/rotated automatically (`secret generate`/`rotate`); one without `generate` is an
273
+ external value you paste in (`secret set`).
274
+
275
+ ### `spfn setup icons`
276
+
277
+ Install and configure SVGR for SVG-as-component imports (Next.js only).
278
+
279
+ ---
280
+
281
+ ## Scaffold structure
282
+
283
+ `spfn init` (and `create`, which calls it) produces:
284
+
285
+ ```
286
+ src/
287
+ app/api/rpc/[routeName]/route.ts # RPC proxy — re-exports { GET, POST } from @spfn/core/nextjs/server
288
+ generated/route-map.ts # generated by codegen (run `spfn codegen run` if missing)
289
+ lib/
290
+ api-client.ts # createApi<AppRouter>() — the type-safe client
291
+ server/
292
+ router.ts # defineRouter({ ...routes }) → export type AppRouter
293
+ server.config.ts # defineServerConfig().port(8790).host('0.0.0.0').routes(appRouter)
294
+ config/env.config.ts # environment schema
295
+ entities/ # Drizzle tables (example.entity.ts, config.ts)
296
+ repositories/ # BaseRepository subclasses (example.repository.ts)
297
+ routes/ # route DSL handlers: root.ts, health.ts, examples.ts
298
+ tsconfig.json, tsup.config.ts
299
+ .spfnrc.ts # codegen config (route-map generator)
300
+ spfn.config.js # deployment config (subdomain/region/domains) — committed
301
+ docker-compose.yml # Postgres + Redis (dev)
302
+ docker-compose.production.yml
303
+ Dockerfile, .dockerignore
304
+ next.config.ts # patched when auth is enabled: /_auth/:path* rewrite → SPFN API
305
+ .env.example # committed reference — every key, placeholder values
306
+ .env.local # generated, gitignored (Next.js-facing URLs)
307
+ .env.server # generated, gitignored (server secrets: DB, cache)
177
308
  ```
178
309
 
179
- ### Setup Features
180
- ```bash
181
- spfn setup icons # Setup SVGR for SVG icon management
310
+ `init` also patches `package.json` (scripts: `spfn:dev`, `spfn:server`, `spfn:next`,
311
+ `spfn:build`, `spfn:start`, `codegen`; deps: `@spfn/core`, `spfn`, `drizzle-orm`,
312
+ `@sinclair/typebox`, `concurrently`, etc.), excludes `src/server` from the root
313
+ `tsconfig.json` (Vercel compat), and adds `.spfn/`, `.env.local`, `.env.server` to
314
+ `.gitignore`.
315
+
316
+ ### Route DSL (the current architecture)
317
+
318
+ Routes are defined with the `route` builder and collected by `defineRouter`. There is no
319
+ separate "contract" layer — the router's type *is* the contract; the client infers from it.
320
+
321
+ ```ts
322
+ // src/server/routes/examples.ts
323
+ import { route } from '@spfn/core/route';
324
+ import { Type } from '@sinclair/typebox';
325
+
326
+ export const getExample = route.get('/examples/:id')
327
+ .input({ params: Type.Object({ id: Type.String() }) })
328
+ .handler(async (c) =>
329
+ {
330
+ const { params } = await c.data();
331
+ return { id: params.id };
332
+ });
182
333
  ```
183
334
 
184
- ### Utilities
185
- ```bash
186
- spfn key # Generate encryption key for .env
335
+ ```ts
336
+ // src/server/router.ts
337
+ import { defineRouter } from '@spfn/core/route';
338
+ import { getExample } from './routes/examples';
339
+
340
+ export const appRouter = defineRouter({ getExample });
341
+ export type AppRouter = typeof appRouter;
187
342
  ```
188
343
 
189
- ## Configuration
190
-
191
- ### spfn.config.js
192
-
193
- SPFN uses `spfn.config.js` for deployment configuration with full JSDoc type support for IDE autocomplete.
194
-
195
- **Basic Configuration:**
196
- ```javascript
197
- /**
198
- * @type {import('spfn').SpfnConfig}
199
- */
200
- export default {
201
- packageManager: 'pnpm',
202
- deployment: {
203
- // Your app's subdomain on spfn.app
204
- // Creates region-specific domains:
205
- // - myapp.us.spfn.app (Next.js)
206
- // - api-myapp.us.spfn.app (API)
207
- subdomain: 'myapp',
208
-
209
- // Optional: Deployment region (defaults to 'us')
210
- // Available: 'us' (Virginia, default), 'kr' (Seoul), 'jp', 'sg', 'eu' (coming soon)
211
- region: 'us',
212
-
213
- // Optional: Add custom domains
214
- customDomains: {
215
- nextjs: ['www.example.com', 'example.com'],
216
- spfn: ['api.example.com']
217
- },
218
-
219
- // Optional: Environment variables for both Next.js and SPFN backend
220
- // ⚠️ WARNING: These values are committed to Git
221
- // Do NOT put sensitive credentials here!
222
- env: {
223
- NEXT_PUBLIC_API_URL: 'https://api-myapp.us.spfn.app',
224
- NODE_ENV: 'production'
225
- }
226
- }
227
- }
344
+ ```ts
345
+ // src/lib/api-client.ts
346
+ import { createApi } from '@spfn/core/nextjs';
347
+ import type { AppRouter } from '@/server/router';
348
+
349
+ export const api = createApi<AppRouter>();
350
+ const example = await api.getExample.call({ params: { id: '123' } });
228
351
  ```
229
352
 
230
- **Features:**
231
- - **JSDoc Type Hints** - IDE autocomplete via `@type {import('spfn').SpfnConfig}`
232
- - **Multi-Region Deployment** - Deploy to Seoul (kr), Virginia (us), and more
233
- - **Dual Domain Setup** - Automatic `{subdomain}.{region}.spfn.app` and `api-{subdomain}.{region}.spfn.app`
234
- - **Custom Domains** - Support for multiple custom domains
235
- - **Environment Variables** - Shared between Next.js and SPFN backend
236
- - **ESM/CJS Support** - Works with both module systems
353
+ Client calls go through the Next.js RPC proxy (`/api/rpc/[routeName]`), which forwards to
354
+ the SPFN API with cookie forwarding and interceptors, resolving routes via the generated
355
+ `route-map.ts`.
356
+
357
+ ---
237
358
 
238
- **Security Note:**
239
- - `spfn.config.js` is committed to Git
240
- - Only use for non-sensitive configuration
241
- - For secrets (DB passwords, API keys), use CI/CD secrets management
359
+ ## Deployment
242
360
 
243
- ## Documentation
361
+ `spfn build` then `spfn start`, or use the generated Docker files.
244
362
 
245
- For complete documentation and guides, see:
246
- - **[SPFN Framework](../../README.md)** - Getting started
247
- - **[@spfn/core](../core/README.md)** - API reference and core concepts
363
+ ```bash
364
+ # Build + run locally
365
+ pnpm spfn:build
366
+ pnpm spfn:start # Next.js :3790 + SPFN API :8790
248
367
 
249
- ## Requirements
368
+ # Docker (single image runs both)
369
+ docker compose -f docker-compose.production.yml up --build -d
370
+ ```
250
371
 
251
- - Node.js 18+
252
- - Next.js 15+ (App Router)
253
- - PostgreSQL (optional: Redis)
372
+ The Dockerfile (`node:22-alpine`) installs with `pnpm --frozen-lockfile`, runs
373
+ `pnpm run spfn:build`, prunes dev deps, exposes `3790`/`8790`, health-checks
374
+ `http://localhost:8790/health`, and starts via `pnpm run spfn:start`.
254
375
 
255
- ## Links
376
+ Run migrations against the target DB before/with deploy:
377
+
378
+ ```bash
379
+ docker exec <container> npx spfn db migrate
380
+ ```
256
381
 
257
- - 🌐 Website: [superfunction.xyz](https://superfunction.xyz)
258
- - 📦 npm: [spfn](https://npmjs.com/package/spfn) (CLI)
259
- - 📦 npm: [@spfn/core](https://npmjs.com/package/@spfn/core) (Core)
260
- - 💬 GitHub: [spfn/spfn](https://github.com/spfn/spfn)
382
+ `spfn.config.js` (committed) configures the managed `*.spfn.app` deployment: `subdomain`,
383
+ `region` (`us` default, `kr`, …), `customDomains`, and non-secret `env`. Its `SpfnConfig`
384
+ type ships from `spfn` (`@type {import('spfn').SpfnConfig}`).
385
+
386
+ ---
387
+
388
+ ## Pitfalls
389
+
390
+ - **`.env.server` is gitignored and server-only.** Put DB/secret values there, not in
391
+ `.env` (committed) and not in `.env.local` (that's Next.js's local file). There is no
392
+ `.env.server.local`. `spfn init` generates `.env.server`; put DB/secret values there.
393
+ Load order is the standard dotenv chain ending with `.env.server`.
394
+ - **Never commit secrets in `spfn.config.js`.** It's checked into Git; its `env` block is
395
+ for non-sensitive values only. Use CI/CD secret management for credentials.
396
+ - **`spfn dev` does not hot-reload by default.** Add `--watch` to restart on `src/server`
397
+ changes.
398
+ - **`spfn start` needs a prior `spfn build`.** It hard-fails without `.spfn/server`,
399
+ `.spfn/prod-server.mjs`, and `.next`.
400
+ - **Destructive DB commands are guarded.** `db drop` double-confirms (and verifies the
401
+ target); `db push` applies the selected statements in one transaction and withholds
402
+ destructive ones unless `--force`/confirmed. Prefer `db generate` + `db migrate` for
403
+ production; `db push` is dev-only.
404
+ - **`spfn add` requires a scoped package name** (must contain `/`) and only applies
405
+ migrations when `DATABASE_URL` is set — otherwise it skips with a hint.
406
+ - **Package manager is auto-detected from lockfiles.** If detection is wrong (e.g. mixed
407
+ lockfiles), pass `--pm` to `create`. In a pnpm workspace, `create` installs from the
408
+ workspace root, not the new project dir.
409
+ - **Regenerate the route map after route changes outside dev.** If
410
+ `src/generated/route-map.ts` is missing or stale, run `spfn codegen run` — the RPC proxy
411
+ depends on it.
412
+
413
+ ---
414
+
415
+ ## Related
416
+
417
+ - `@spfn/core` — server, route DSL, codegen, db, client runtime.
418
+ - Project root README — framework overview and getting started.
419
+ </content>
420
+ </invoke>