spfn 0.3.0-beta.3 → 0.3.0-beta.5
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/README.md +72 -16
- package/dist/index.js +1271 -27
- package/dist/templates/README.md +21 -6
- package/dist/templates/modes/full/src/server/router.ts +3 -3
- package/dist/templates/modes/full/src/server/routes/ops.ts +52 -0
- package/dist/templates/modes/full/src/server/routes/root.ts +1 -1
- package/package.json +2 -1
- package/dist/templates/modes/full/src/server/config/env.config.ts +0 -23
- package/dist/templates/modes/full/src/server/mcp.ts +0 -75
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`spfn` takes a Next.js idea from prototype to production with a consistent full-stack
|
|
4
4
|
architecture. It can scaffold either a core-only backend or a production baseline with
|
|
5
|
-
authentication, internationalization, and
|
|
5
|
+
authentication, internationalization, and a terminal operations surface, then runs the
|
|
6
6
|
dev/build/start lifecycle, database tooling, RPC codegen, and environment validation.
|
|
7
7
|
|
|
8
8
|
Consistent is the point rather than a nicety: what it scaffolds is one fixed shape per
|
|
@@ -31,7 +31,7 @@ is not supported — see [the root README](../../README.md#what-do-i-need-instal
|
|
|
31
31
|
## Usage
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
# Prototype-to-Production baseline: core + auth + i18n +
|
|
34
|
+
# Prototype-to-Production baseline: core + auth + i18n + ops
|
|
35
35
|
npx spfn@beta create my-app --mode full
|
|
36
36
|
cd my-app
|
|
37
37
|
docker compose up -d # Postgres + Redis
|
|
@@ -54,7 +54,8 @@ with `--pm`. In a pnpm workspace, `create` installs from the workspace root.
|
|
|
54
54
|
## Commands
|
|
55
55
|
|
|
56
56
|
Registered top-level commands: `create`, `init`, `add`, `dev`, `build`, `start`,
|
|
57
|
-
`provision`, `codegen`, `contract`, `key`, `setup`, `db`, `env`, `ops`, `secret
|
|
57
|
+
`provision`, `codegen`, `contract`, `key`, `setup`, `db`, `env`, `ops`, `secret`,
|
|
58
|
+
`cloud`.
|
|
58
59
|
|
|
59
60
|
### `spfn create <name>`
|
|
60
61
|
|
|
@@ -71,7 +72,7 @@ pass `--mode full`.
|
|
|
71
72
|
|--------|-------------|
|
|
72
73
|
| `--pm <manager>` | Force package manager: `npm` \| `pnpm` \| `yarn` \| `bun` |
|
|
73
74
|
| `--shadcn` | Also run `shadcn init` |
|
|
74
|
-
| `--mode <mode>` | `bare` (core only) \| `full` (core, auth, i18n,
|
|
75
|
+
| `--mode <mode>` | `bare` (core only) \| `full` (core, auth, i18n, ops) |
|
|
75
76
|
| `--skip-install` | Skip dependency install |
|
|
76
77
|
| `--skip-git` | Skip `git init` |
|
|
77
78
|
| `-y, --yes` | Skip prompts, use defaults |
|
|
@@ -86,7 +87,7 @@ already exists). See [Scaffold structure](#scaffold-structure) for what lands on
|
|
|
86
87
|
|
|
87
88
|
| Option | Description |
|
|
88
89
|
|--------|-------------|
|
|
89
|
-
| `--mode <mode>` | `bare` (core only) \| `full` (core, auth, i18n,
|
|
90
|
+
| `--mode <mode>` | `bare` (core only) \| `full` (core, auth, i18n, ops) |
|
|
90
91
|
| `-y, --yes` | Skip prompts, use defaults |
|
|
91
92
|
|
|
92
93
|
Generated projects pin `drizzle-orm` and `drizzle-kit` to `1.0.0-rc.4`, matching
|
|
@@ -384,6 +385,31 @@ Schema-driven: a secret declared with `envSecret({ generate: 'base64url32' })` c
|
|
|
384
385
|
minted/rotated automatically (`secret generate`/`rotate`); one without `generate` is an
|
|
385
386
|
external value you paste in (`secret set`).
|
|
386
387
|
|
|
388
|
+
### `spfn cloud`
|
|
389
|
+
|
|
390
|
+
Free-tier management for apps deployed to your own **Vercel Hobby + Supabase Free**
|
|
391
|
+
accounts: see the plan limits, watch live usage against them, keep the Supabase
|
|
392
|
+
project from pausing, and sync env vars/API keys. Account tokens and key values live
|
|
393
|
+
in the OS keychain and never appear in command output.
|
|
394
|
+
|
|
395
|
+
| Subcommand | Description |
|
|
396
|
+
|------------|-------------|
|
|
397
|
+
| `cloud link` | Connect accounts: Vercel access token + Supabase personal access token (masked prompt or `VERCEL_TOKEN`/`SUPABASE_ACCESS_TOKEN` env), pick the project on each side. Identifiers land in gitignored `.spfn/cloud.json`; tokens go to the keychain |
|
|
398
|
+
| `cloud limits` | The free-plan limits (constants verified against the official docs, date shown) — works before `link` |
|
|
399
|
+
| `cloud usage` | Current usage: Vercel billing feed (rolling 30 days), Supabase DB size + last-24h API requests |
|
|
400
|
+
| `cloud status` | Usage measured against the limits on one screen; items at ≥80% get a migration warning |
|
|
401
|
+
| `cloud keepalive` | Daily cron hitting `/api/backend/_core/health?detailed=true` (the detailed check runs a DB query, which is what prevents the ~7-idle-day pause). Vercel cron by default; `--github-actions --url <deployed-url>` for a workflow instead |
|
|
402
|
+
| `cloud env pull` | Supabase keys → local: project URL + anon key into `.env.local`, service-role key into the keychain (`.env.server` gets a reference). `--db-url` also composes `DATABASE_URL` (prompts for the DB password) |
|
|
403
|
+
| `cloud env push KEY…` | Push local env values to the Vercel project env by name. Values resolve from `.env`/`.env.local`/`.env.server`+keychain; everything is sent encrypted except `NEXT_PUBLIC_*` |
|
|
404
|
+
|
|
405
|
+
Free-tier behavior worth knowing (also printed by `cloud limits`): Vercel Hobby
|
|
406
|
+
allows one cron at most once per day and pauses a capability when its rolling
|
|
407
|
+
30-day limit is hit (it never bills); Supabase Free quota is summed per
|
|
408
|
+
organization (except DB size), and org totals like egress/MAU have no public API —
|
|
409
|
+
`cloud status` shows per-item numbers and points at the dashboard for the rest.
|
|
410
|
+
Hobby is limited to personal, non-commercial use — a monetized app needs Vercel Pro
|
|
411
|
+
or a migration off the free tier.
|
|
412
|
+
|
|
387
413
|
### `spfn setup icons`
|
|
388
414
|
|
|
389
415
|
Install and configure SVGR for SVG-as-component imports (Next.js only).
|
|
@@ -418,6 +444,29 @@ listSignups GET /_ops/signups
|
|
|
418
444
|
Add `--json` for the raw JSON Schema. The server still validates every call — `--describe`
|
|
419
445
|
reports what it will accept, and the app's answer decides.
|
|
420
446
|
|
|
447
|
+
#### Capability modules
|
|
448
|
+
|
|
449
|
+
An app can also mount ops commands a package described, with
|
|
450
|
+
[`defineOpsModule`](../core/README.md#can-a-package-ship-ops-commands). Those commands are
|
|
451
|
+
named `<module>.<command>` and carry a summary, an effect and their scopes, so the CLI can
|
|
452
|
+
group them and say what each one does. From **0.3.0-beta.5**:
|
|
453
|
+
|
|
454
|
+
```bash
|
|
455
|
+
spfn ops modules # what is mounted, and from where
|
|
456
|
+
spfn ops modules --json # same, machine-readable
|
|
457
|
+
spfn ops list --module ledger # just that module's commands
|
|
458
|
+
spfn ops call ledger.compact --yes # effect=destructive needs this
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
`spfn ops call` refuses a command the app declared `effect: destructive` unless `--yes` is
|
|
462
|
+
given. It refuses the same way when the app announced module metadata this CLI could not
|
|
463
|
+
validate: the effect is then unknown rather than absent, and an unknown effect is not
|
|
464
|
+
treated as a safe one. The command still lists — an operator reading a short list would
|
|
465
|
+
otherwise take it for the app's whole surface — and the warning names what was dropped.
|
|
466
|
+
|
|
467
|
+
Everything in the manifest is the app's own text written to your terminal, so control
|
|
468
|
+
characters in it are replaced before anything is printed.
|
|
469
|
+
|
|
421
470
|
The app URL comes from `--app` or `SPFN_OPS_APP`, and it must be **https** — every one of
|
|
422
471
|
these commands carries a secret, and `token issue` carries an administrator's password.
|
|
423
472
|
`http` is accepted only against `localhost`, `127.0.0.1` and `::1`, where there is no
|
|
@@ -498,24 +547,30 @@ src/
|
|
|
498
547
|
app/auth/callback/page.tsx # OAuth session handoff
|
|
499
548
|
i18n/catalogs.ts # application-owned en/ko starter messages
|
|
500
549
|
i18n/server.ts # configured server-side i18n registry
|
|
501
|
-
server/
|
|
502
|
-
server/router.ts # authRouter +
|
|
550
|
+
server/routes/ops.ts # ops routes under /_ops + the manifest `spfn ops` reads
|
|
551
|
+
server/router.ts # authRouter + opsRouter + global authenticate
|
|
503
552
|
server/server.config.ts # createAuthLifecycle + i18n startup
|
|
504
553
|
next.config.ts # /_auth/* callback rewrite
|
|
505
554
|
.env.local # generated auth session secret (gitignored)
|
|
506
|
-
.env.server # auth keyring
|
|
555
|
+
.env.server # auth keyring (gitignored)
|
|
507
556
|
```
|
|
508
557
|
|
|
509
558
|
The full RPC proxy imports the auth interceptor and merges `authRouteMap`. Internal auth
|
|
510
|
-
|
|
559
|
+
keys are generated with cryptographic randomness in ignored local env files;
|
|
511
560
|
`.env.example` contains placeholders only. Add only the provider keys you use, then run
|
|
512
|
-
`pnpm spfn db migrate`.
|
|
513
|
-
|
|
561
|
+
`pnpm spfn db migrate`.
|
|
562
|
+
|
|
563
|
+
Operating the app is [`spfn ops`](#spfn-ops), not a dashboard: the starter
|
|
564
|
+
`src/server/routes/ops.ts` exposes two read commands, and `spfn ops` discovers them from
|
|
565
|
+
the running server's manifest. Issuing the first token signs in as an administrator, so
|
|
566
|
+
uncomment `SPFN_AUTH_ADMIN_ACCOUNTS` in `.env.server` and restart before
|
|
567
|
+
`spfn ops token issue`. The ops surface adds no dependency — the router comes from
|
|
568
|
+
`@spfn/core/ops` and the tokens from `@spfn/auth`.
|
|
514
569
|
|
|
515
570
|
`init` also patches `package.json` (scripts: `spfn:dev`, `spfn:server`, `spfn:next`,
|
|
516
571
|
`spfn:build`, `spfn:start`, `codegen`; deps: `@spfn/core`, `spfn`, `drizzle-orm`,
|
|
517
572
|
`@sinclair/typebox`, `concurrently`, etc.; full also adds `@spfn/auth`, `@spfn/i18n`,
|
|
518
|
-
|
|
573
|
+
auth's `@spfn/notification` peer, and a Node `>=20.0.0` engine when the
|
|
519
574
|
existing range still permits older Node versions), excludes `src/server` from the root
|
|
520
575
|
`tsconfig.json` (Vercel compat), and adds `.spfn/`, `.env.local`, `.env.server` to
|
|
521
576
|
`.gitignore`.
|
|
@@ -621,8 +676,8 @@ type ships from `spfn` (`@type {import('spfn').SpfnConfig}`).
|
|
|
621
676
|
something with an AI coding agent and now want a real backend under it, `init` is the one.
|
|
622
677
|
|
|
623
678
|
**`bare` or `full`?**
|
|
624
|
-
`full` is the recommended baseline: core, auth, i18n and
|
|
625
|
-
working authenticated app on day one. `bare` is core only — the architecture with nothing
|
|
679
|
+
`full` is the recommended baseline: core, auth, i18n and the ops surface wired together, so
|
|
680
|
+
you get a working authenticated app on day one, operable from the terminal. `bare` is core only — the architecture with nothing
|
|
626
681
|
else decided. Automation should always pass `--mode` explicitly, because a `--yes` run
|
|
627
682
|
without one still produces `bare` for backward compatibility.
|
|
628
683
|
|
|
@@ -637,7 +692,7 @@ pointing at your own PostgreSQL works too. PostgreSQL itself is not optional.
|
|
|
637
692
|
|
|
638
693
|
**Which Node version do I need?**
|
|
639
694
|
20 or later, in both modes. `@spfn/core` runs on `@hono/node-server` 2, which declares
|
|
640
|
-
that floor, and full mode's
|
|
695
|
+
that floor, and full mode's `@spfn/auth` needs the same.
|
|
641
696
|
|
|
642
697
|
**When do I have to run codegen by hand?**
|
|
643
698
|
Whenever routes change outside `spfn dev`, which runs a codegen watcher for you. A stale or
|
|
@@ -685,5 +740,6 @@ committed.
|
|
|
685
740
|
|
|
686
741
|
- [`@spfn/core`](../core/README.md) — server, route DSL, codegen, db, client runtime.
|
|
687
742
|
- [`@spfn/auth`](../auth/README.md) — what `--mode full` wires in for accounts and roles.
|
|
688
|
-
- [`@spfn/mcp`](../mcp/README.md) —
|
|
743
|
+
- [`@spfn/mcp`](../mcp/README.md) — an agent-facing MCP endpoint, added on demand with
|
|
744
|
+
`spfn add @spfn/mcp`.
|
|
689
745
|
- Project root README — framework overview and getting started.
|