Skip to content

Commit d940110

Browse files
committed
chore: clean registry install surfaces
Resolve the Next SaaS preset/module ambiguity, improve planned module install warnings, document Stripe env requirements, and acknowledge the shadcn registry/source-block inspiration.
1 parent a2f0890 commit d940110

27 files changed

Lines changed: 313 additions & 83 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Modules install as source files in your app. You review the code, own the code,
1313
> [!NOTE]
1414
> The registry is the product. Presets are only curated bundles of modules.
1515
16+
StackFoundry is inspired by the shadcn registry/source-block model and applies that idea to production SaaS systems: modules ship as editable source, not opaque dependencies.
17+
1618
## Why Use It
1719

1820
| Problem | StackFoundry gives you |
@@ -64,7 +66,7 @@ Current source registry coverage:
6466

6567
| Area | Examples |
6668
| --- | --- |
67-
| Foundation | `next-saas`, `sidebar-shell`, `t3-env`, `quality-tooling`, `theme-system` |
69+
| Foundation | `next-saas-shell`, `sidebar-shell`, `t3-env`, `quality-tooling`, `theme-system` |
6870
| Database | `drizzle-postgres`, `drizzle-relations`, `neon-postgres`, `supabase-postgres`, `cloudflare-d1` |
6971
| Auth and tenancy | `clerk-auth`, `orgs-rbac`, `account-modes`, `invites`, `enterprise-sso`, `scim-provisioning` |
7072
| Billing | `stripe-billing`, `billing-core`, `entitlements`, `credit-wallet`, `one-time-purchases`, `tax-vat` |

apps/cli/src/cli.mjs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -587,13 +587,13 @@ async function saveInstallManifest(target, manifest) {
587587
await writeFile(path.join(dir, "installed.json"), `${JSON.stringify(manifest, null, 2)}\n`);
588588
}
589589

590-
async function addModule(name, flags, visited = new Set()) {
590+
async function addModule(name, flags, visited = new Set(), context = {}) {
591591
if (visited.has(name)) return;
592592
visited.add(name);
593593

594594
const { dir, manifest } = await getModule(name);
595595
for (const dependency of manifest.registryDependencies) {
596-
await addModule(dependency, flags, visited);
596+
await addModule(dependency, flags, visited, context);
597597
}
598598

599599
const filesDir = path.join(dir, "files");
@@ -602,6 +602,12 @@ async function addModule(name, flags, visited = new Set()) {
602602
const installed = await loadInstallManifest(flags.target);
603603
const installedFiles = {};
604604

605+
if (sourceFiles.length === 0 && !context.presetName) {
606+
console.log(
607+
`warning: ${manifest.name} is ${manifest.status} and currently installs maintenance metadata only`,
608+
);
609+
}
610+
605611
for (const source of sourceFiles) {
606612
const relative = path.relative(filesDir, source);
607613
const normalized = toPosixPath(relative);
@@ -660,7 +666,7 @@ async function addPreset(name, flags) {
660666
const visited = new Set();
661667
console.log(`${flags.dryRun ? "would install" : "installing"} preset ${preset.name}`);
662668
for (const moduleName of preset.modules) {
663-
await addModule(moduleName, flags, visited);
669+
await addModule(moduleName, flags, visited, { presetName: preset.name });
664670
}
665671
}
666672

@@ -683,6 +689,12 @@ async function addRegistryItem(specifier, flags, visited = new Set()) {
683689
const installedFiles = {};
684690
console.log(`${flags.dryRun ? "would install" : "installing"} registry item ${item.name}`);
685691

692+
if ((item.files?.length ?? 0) === 0 && item.meta?.category !== "preset") {
693+
console.log(
694+
`warning: ${item.name} is ${item.meta?.status ?? "planned"} and currently installs maintenance metadata only`,
695+
);
696+
}
697+
686698
for (const file of item.files ?? []) {
687699
if (!file.content) throw new Error(`${item.name}: ${file.path} is missing embedded content`);
688700

apps/web/app/docs/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ const moduleGroups = [
8787
category: "Foundation",
8888
summary: "Small base scaffolds and product shell pieces.",
8989
modules: [
90-
"next-saas",
90+
"next-saas-shell",
9191
"sidebar-shell",
9292
"t3-env",
9393
"theme-system",
@@ -226,7 +226,7 @@ const moduleGroups = [
226226
];
227227

228228
const moduleMeta: Record<string, { tone: string; domain?: string }> = {
229-
"next-saas": { tone: "foundation", domain: "nextjs.org" },
229+
"next-saas-shell": { tone: "foundation", domain: "nextjs.org" },
230230
"drizzle-postgres": { tone: "database", domain: "drizzle.team" },
231231
"drizzle-relations": { tone: "database", domain: "drizzle.team" },
232232
"drizzle-soft-delete": { tone: "database", domain: "drizzle.team" },

apps/web/app/registry/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ const providerOptions = [
293293
domain: "nextjs.org",
294294
logo: "nextjs",
295295
role: "App framework",
296-
modules: ["next-saas", "docs-fumadocs"],
296+
modules: ["next-saas-shell", "docs-fumadocs"],
297297
},
298298
{
299299
name: "React",

docs/modules.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Module-specific skills stay focused on ownership, installed files, environment v
2727

2828
### Foundation
2929

30-
- `next-saas`
30+
- `next-saas-shell`
3131
- `t3-env`
3232
- `sidebar-shell`
3333
- `geist-nova-theme`

docs/providers.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Providers map into shared domain systems. They should not become one-off code is
2121

2222
- Vercel first for Next.js applications
2323
- Cloudflare as an optional edge/workers platform
24-
- Railway, Fly, and Render as future adapters
24+
- Railway, Fly, and Render as optional deploy adapters
2525

2626
### Database and Storage
2727

@@ -46,4 +46,4 @@ Providers map into shared domain systems. They should not become one-off code is
4646
### Optional AI
4747

4848
- Vercel AI SDK and AI Gateway for AI modules
49-
- Provider-specific model routing belongs behind module adapters
49+
- Provider model routing belongs behind module adapters

docs/registry.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ Each module has:
2828

2929
StackFoundry follows a source registry pattern: source blocks are described by JSON, files are copied into the consumer app, and the consumer owns the result.
3030

31+
The model is inspired by the shadcn registry/source-block approach, then extended for production SaaS modules with routes, server helpers, schema files, env notes, docs, verification checklists, and maintenance skills.
32+
3133
StackFoundry adapts that model for production SaaS systems instead of UI-only components. A block may include routes, server helpers, schema files, env examples, docs, tests/checklists, and maintainer skill guidance.
3234

3335
## Registry Principles

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"test": "pnpm test:registry",
2727
"test:brand": "node scripts/smoke-brand.mjs",
2828
"test:registry": "pnpm test:registry:dry && pnpm test:registry:install",
29-
"test:registry:dry": "rm -rf /tmp/stackfoundry-test && mkdir -p /tmp/stackfoundry-test && pnpm cli list >/dev/null && pnpm cli presets >/dev/null && pnpm cli add next-saas --target /tmp/stackfoundry-test/next-saas-module >/dev/null && test -f /tmp/stackfoundry-test/next-saas-module/apps/web/src/components/stackfoundry/app-shell.tsx && pnpm cli add auth-core --target /tmp/stackfoundry-test/auth-core >/dev/null && test -f /tmp/stackfoundry-test/auth-core/docs/auth-architecture.md && pnpm cli add cloudflare-pages --target /tmp/stackfoundry-test/cloudflare-pages >/dev/null && test -f /tmp/stackfoundry-test/cloudflare-pages/apps/web/src/lib/stackfoundry/providers/cloudflare-pages.ts && pnpm cli add railway-deploy --target /tmp/stackfoundry-test/railway-deploy >/dev/null && test -f /tmp/stackfoundry-test/railway-deploy/apps/web/src/lib/stackfoundry/providers/railway-deploy.ts && pnpm cli add api-keys --target /tmp/stackfoundry-test >/dev/null && pnpm cli diff api-keys --target /tmp/stackfoundry-test >/dev/null && pnpm cli add preset next-saas --target /tmp/stackfoundry-test --dry-run >/dev/null && pnpm cli add preset provider-adapters --target /tmp/stackfoundry-test --dry-run >/dev/null && pnpm cli add preset saas-coverage --target /tmp/stackfoundry-test --dry-run >/dev/null && pnpm cli add public/r/api-keys.json --target /tmp/stackfoundry-test --dry-run >/dev/null && pnpm cli add public/r/provider-adapters.json --target /tmp/stackfoundry-test --dry-run >/dev/null",
29+
"test:registry:dry": "rm -rf /tmp/stackfoundry-test && mkdir -p /tmp/stackfoundry-test && pnpm cli list >/dev/null && pnpm cli presets >/dev/null && pnpm cli add next-saas-shell --target /tmp/stackfoundry-test/next-saas-module >/dev/null && test -f /tmp/stackfoundry-test/next-saas-module/apps/web/src/components/stackfoundry/app-shell.tsx && pnpm cli add auth-core --target /tmp/stackfoundry-test/auth-core >/dev/null && test -f /tmp/stackfoundry-test/auth-core/docs/auth-architecture.md && pnpm cli add cloudflare-pages --target /tmp/stackfoundry-test/cloudflare-pages >/dev/null && test -f /tmp/stackfoundry-test/cloudflare-pages/apps/web/src/lib/stackfoundry/providers/cloudflare-pages.ts && pnpm cli add railway-deploy --target /tmp/stackfoundry-test/railway-deploy >/dev/null && test -f /tmp/stackfoundry-test/railway-deploy/apps/web/src/lib/stackfoundry/providers/railway-deploy.ts && pnpm cli add api-keys --target /tmp/stackfoundry-test >/dev/null && pnpm cli diff api-keys --target /tmp/stackfoundry-test >/dev/null && pnpm cli add preset next-saas --target /tmp/stackfoundry-test --dry-run >/dev/null && pnpm cli add preset provider-adapters --target /tmp/stackfoundry-test --dry-run >/dev/null && pnpm cli add preset saas-coverage --target /tmp/stackfoundry-test --dry-run >/dev/null && pnpm cli add public/r/api-keys.json --target /tmp/stackfoundry-test --dry-run >/dev/null && pnpm cli add public/r/provider-adapters.json --target /tmp/stackfoundry-test --dry-run >/dev/null",
3030
"test:registry:install": "rm -rf /tmp/stackfoundry-install && mkdir -p /tmp/stackfoundry-install/next-saas /tmp/stackfoundry-install/provider-adapters /tmp/stackfoundry-install/cloudflare-native /tmp/stackfoundry-install/vercel-native /tmp/stackfoundry-install/saas-coverage /tmp/stackfoundry-install/registry-api-keys /tmp/stackfoundry-install/registry-adapters /tmp/stackfoundry-install/url-api-keys /tmp/stackfoundry-install/url-adapters && pnpm cli add preset next-saas --target /tmp/stackfoundry-install/next-saas >/dev/null && pnpm cli add preset provider-adapters --target /tmp/stackfoundry-install/provider-adapters >/dev/null && pnpm cli add preset cloudflare-native --target /tmp/stackfoundry-install/cloudflare-native >/dev/null && pnpm cli add preset vercel-native --target /tmp/stackfoundry-install/vercel-native >/dev/null && pnpm cli add preset saas-coverage --target /tmp/stackfoundry-install/saas-coverage >/dev/null && pnpm cli add public/r/api-keys.json --target /tmp/stackfoundry-install/registry-api-keys >/dev/null && pnpm cli add public/r/provider-adapters.json --target /tmp/stackfoundry-install/registry-adapters >/dev/null && pnpm cli add https://stackfoundry.dev/r/api-keys.json --target /tmp/stackfoundry-install/url-api-keys >/dev/null && pnpm cli add https://stackfoundry.dev/r/provider-adapters.json --target /tmp/stackfoundry-install/url-adapters >/dev/null",
3131
"cli": "node apps/cli/src/cli.mjs",
3232
"registry:list": "pnpm cli list",

public/r/github-integration.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"path": "apps/web/src/lib/github/app.ts",
1717
"type": "registry:file",
1818
"target": "apps/web/src/lib/github/app.ts",
19-
"content": "import \"server-only\";\n\nimport { App } from \"octokit\";\n\nfunction requireEnv(key: string) {\n const value = process.env[key];\n if (!value) throw new Error(`${key} is required.`);\n return value;\n}\n\nexport function createGitHubApp() {\n return new App({\n appId: requireEnv(\"GITHUB_APP_ID\"),\n privateKey: requireEnv(\"GITHUB_PRIVATE_KEY\").replace(/\\n/g, \"\n\"),\n });\n}\n"
19+
"content": "import \"server-only\";\n\nimport { App } from \"octokit\";\n\nfunction requireEnv(key: string) {\n const value = process.env[key];\n if (!value) throw new Error(`${key} is required.`);\n return value;\n}\n\nexport function createGitHubApp() {\n return new App({\n appId: requireEnv(\"GITHUB_APP_ID\"),\n privateKey: requireEnv(\"GITHUB_PRIVATE_KEY\").replace(/\\\\n/g, \"\\n\"),\n });\n}\n"
2020
},
2121
{
2222
"path": "apps/web/src/app/api/webhooks/github/route.ts",

0 commit comments

Comments
 (0)