create-skit 0.1.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.
- package/README.md +36 -0
- package/bin/create-skit.mjs +1064 -0
- package/lib/module-application.mjs +281 -0
- package/lib/module-resolver.mjs +179 -0
- package/modules/README.md +22 -0
- package/modules/ai-dx/files/AGENTS.md +116 -0
- package/modules/ai-dx/files/ARCHITECTURE.md +103 -0
- package/modules/ai-dx/module.json +14 -0
- package/modules/ai-dx-claude/files/CLAUDE.md +8 -0
- package/modules/ai-dx-claude/module.json +13 -0
- package/modules/ai-dx-cursor/files/.cursor/rules/auth.mdc +53 -0
- package/modules/ai-dx-cursor/files/.cursor/rules/database.mdc +48 -0
- package/modules/ai-dx-cursor/files/.cursor/rules/env.mdc +43 -0
- package/modules/ai-dx-cursor/files/.cursor/rules/nextjs.mdc +58 -0
- package/modules/ai-dx-cursor/files/.cursor/rules/project.mdc +33 -0
- package/modules/ai-dx-cursor/files/.cursor/rules/testing.mdc +55 -0
- package/modules/ai-dx-cursor/module.json +18 -0
- package/modules/ai-dx-gemini/files/.gemini/GEMINI.md +5 -0
- package/modules/ai-dx-gemini/module.json +13 -0
- package/modules/auth-core/module.json +8 -0
- package/modules/auth-github/module.json +20 -0
- package/modules/billing-polar/module.json +20 -0
- package/modules/billing-stripe/module.json +23 -0
- package/modules/dashboard-shell/files/src/app/globals.css +756 -0
- package/modules/dashboard-shell/files/src/app/settings/page.tsx +67 -0
- package/modules/dashboard-shell/module.json +11 -0
- package/modules/db-pg/module.json +21 -0
- package/modules/db-postgresjs/module.json +21 -0
- package/modules/deploy-docker/files/.dockerignore +19 -0
- package/modules/deploy-docker/files/Dockerfile +25 -0
- package/modules/deploy-docker/module.json +11 -0
- package/modules/email-resend/module.json +21 -0
- package/modules/quality-baseline/module.json +8 -0
- package/modules/testing-baseline/module.json +8 -0
- package/package.json +40 -0
- package/presets/README.md +12 -0
- package/presets/blank.json +67 -0
- package/presets/dashboard.json +67 -0
- package/templates/base-web/.env.example +17 -0
- package/templates/base-web/.github/workflows/ci.yml +34 -0
- package/templates/base-web/.husky/pre-commit +3 -0
- package/templates/base-web/.husky/pre-push +3 -0
- package/templates/base-web/.prettierignore +3 -0
- package/templates/base-web/README.md +42 -0
- package/templates/base-web/drizzle.config.ts +16 -0
- package/templates/base-web/eslint.config.mjs +127 -0
- package/templates/base-web/manifest.json +5 -0
- package/templates/base-web/next-env.d.ts +4 -0
- package/templates/base-web/next.config.ts +5 -0
- package/templates/base-web/package.json +75 -0
- package/templates/base-web/playwright.config.ts +21 -0
- package/templates/base-web/prettier.config.mjs +9 -0
- package/templates/base-web/proxy.ts +23 -0
- package/templates/base-web/src/app/api/auth/[...all]/route.ts +5 -0
- package/templates/base-web/src/app/api/billing/checkout/route.ts +26 -0
- package/templates/base-web/src/app/api/billing/portal/route.ts +25 -0
- package/templates/base-web/src/app/api/email/test/route.ts +28 -0
- package/templates/base-web/src/app/api/webhooks/polar/route.ts +5 -0
- package/templates/base-web/src/app/api/webhooks/stripe/route.ts +5 -0
- package/templates/base-web/src/app/billing/page.tsx +55 -0
- package/templates/base-web/src/app/dashboard/page.tsx +15 -0
- package/templates/base-web/src/app/email/page.tsx +46 -0
- package/templates/base-web/src/app/error.tsx +27 -0
- package/templates/base-web/src/app/globals.css +534 -0
- package/templates/base-web/src/app/layout.tsx +19 -0
- package/templates/base-web/src/app/llms-full.txt/route.ts +158 -0
- package/templates/base-web/src/app/llms.txt/route.ts +59 -0
- package/templates/base-web/src/app/loading.tsx +24 -0
- package/templates/base-web/src/app/not-found.tsx +16 -0
- package/templates/base-web/src/app/page.tsx +5 -0
- package/templates/base-web/src/app/sign-in/page.tsx +14 -0
- package/templates/base-web/src/app/sign-up/page.tsx +14 -0
- package/templates/base-web/src/components/auth/email-auth-form.test.tsx +40 -0
- package/templates/base-web/src/components/auth/email-auth-form.tsx +128 -0
- package/templates/base-web/src/components/auth/sign-out-button.tsx +29 -0
- package/templates/base-web/src/db/index.ts +16 -0
- package/templates/base-web/src/db/schema/auth.ts +4 -0
- package/templates/base-web/src/db/schema/index.ts +2 -0
- package/templates/base-web/src/db/schema/projects.ts +17 -0
- package/templates/base-web/src/db/seeds/index.ts +32 -0
- package/templates/base-web/src/lib/auth-client.ts +5 -0
- package/templates/base-web/src/lib/auth-session.ts +21 -0
- package/templates/base-web/src/lib/auth.ts +23 -0
- package/templates/base-web/src/lib/billing/index.ts +37 -0
- package/templates/base-web/src/lib/billing/providers/polar.ts +80 -0
- package/templates/base-web/src/lib/billing/providers/stripe.ts +77 -0
- package/templates/base-web/src/lib/billing/types.ts +25 -0
- package/templates/base-web/src/lib/email/index.ts +19 -0
- package/templates/base-web/src/lib/email/templates.test.ts +12 -0
- package/templates/base-web/src/lib/email/templates.ts +40 -0
- package/templates/base-web/src/lib/env.ts +83 -0
- package/templates/base-web/tests/e2e/home.spec.ts +8 -0
- package/templates/base-web/tsconfig.json +34 -0
- package/templates/base-web/vitest.config.ts +19 -0
- package/templates/blank/.env.example +16 -0
- package/templates/blank/.github/workflows/ci.yml +34 -0
- package/templates/blank/.husky/pre-commit +3 -0
- package/templates/blank/.husky/pre-push +3 -0
- package/templates/blank/.prettierignore +3 -0
- package/templates/blank/drizzle.config.ts +16 -0
- package/templates/blank/eslint.config.mjs +127 -0
- package/templates/blank/next-env.d.ts +4 -0
- package/templates/blank/next.config.ts +5 -0
- package/templates/blank/package.json +75 -0
- package/templates/blank/playwright.config.ts +21 -0
- package/templates/blank/prettier.config.mjs +9 -0
- package/templates/blank/proxy.ts +28 -0
- package/templates/blank/src/app/api/auth/[...all]/route.ts +5 -0
- package/templates/blank/src/app/api/billing/checkout/route.ts +26 -0
- package/templates/blank/src/app/api/billing/portal/route.ts +25 -0
- package/templates/blank/src/app/api/email/test/route.ts +28 -0
- package/templates/blank/src/app/api/webhooks/polar/route.ts +5 -0
- package/templates/blank/src/app/api/webhooks/stripe/route.ts +5 -0
- package/templates/blank/src/app/billing/page.tsx +70 -0
- package/templates/blank/src/app/email/page.tsx +46 -0
- package/templates/blank/src/app/globals.css +394 -0
- package/templates/blank/src/app/layout.tsx +19 -0
- package/templates/blank/src/app/page.tsx +23 -0
- package/templates/blank/src/app/sign-in/page.tsx +18 -0
- package/templates/blank/src/app/sign-up/page.tsx +18 -0
- package/templates/blank/src/components/auth/email-auth-form.test.tsx +39 -0
- package/templates/blank/src/components/auth/email-auth-form.tsx +109 -0
- package/templates/blank/src/components/auth/sign-out-button.tsx +29 -0
- package/templates/blank/src/db/index.ts +16 -0
- package/templates/blank/src/db/schema/auth.ts +4 -0
- package/templates/blank/src/db/schema/index.ts +2 -0
- package/templates/blank/src/db/schema/projects.ts +17 -0
- package/templates/blank/src/db/seeds/index.ts +28 -0
- package/templates/blank/src/lib/auth-client.ts +5 -0
- package/templates/blank/src/lib/auth-session.ts +11 -0
- package/templates/blank/src/lib/auth.ts +23 -0
- package/templates/blank/src/lib/billing/index.ts +37 -0
- package/templates/blank/src/lib/billing/providers/polar.ts +80 -0
- package/templates/blank/src/lib/billing/providers/stripe.ts +77 -0
- package/templates/blank/src/lib/billing/types.ts +25 -0
- package/templates/blank/src/lib/email/index.ts +19 -0
- package/templates/blank/src/lib/email/templates.test.ts +15 -0
- package/templates/blank/src/lib/email/templates.ts +40 -0
- package/templates/blank/src/lib/env.ts +80 -0
- package/templates/blank/tsconfig.json +34 -0
- package/templates/blank/vitest.config.ts +19 -0
- package/templates/dashboard/.env.example +16 -0
- package/templates/dashboard/.github/workflows/ci.yml +34 -0
- package/templates/dashboard/.husky/pre-commit +3 -0
- package/templates/dashboard/.husky/pre-push +3 -0
- package/templates/dashboard/.prettierignore +3 -0
- package/templates/dashboard/drizzle.config.ts +16 -0
- package/templates/dashboard/eslint.config.mjs +127 -0
- package/templates/dashboard/next-env.d.ts +4 -0
- package/templates/dashboard/next.config.ts +5 -0
- package/templates/dashboard/package.json +75 -0
- package/templates/dashboard/playwright.config.ts +21 -0
- package/templates/dashboard/prettier.config.mjs +9 -0
- package/templates/dashboard/proxy.ts +36 -0
- package/templates/dashboard/src/app/api/auth/[...all]/route.ts +5 -0
- package/templates/dashboard/src/app/api/billing/checkout/route.ts +26 -0
- package/templates/dashboard/src/app/api/billing/portal/route.ts +25 -0
- package/templates/dashboard/src/app/api/email/test/route.ts +28 -0
- package/templates/dashboard/src/app/api/webhooks/polar/route.ts +5 -0
- package/templates/dashboard/src/app/api/webhooks/stripe/route.ts +5 -0
- package/templates/dashboard/src/app/billing/layout.tsx +22 -0
- package/templates/dashboard/src/app/billing/page.tsx +73 -0
- package/templates/dashboard/src/app/dashboard/layout.tsx +22 -0
- package/templates/dashboard/src/app/dashboard/page.tsx +104 -0
- package/templates/dashboard/src/app/email/layout.tsx +22 -0
- package/templates/dashboard/src/app/email/page.tsx +54 -0
- package/templates/dashboard/src/app/globals.css +1357 -0
- package/templates/dashboard/src/app/layout.tsx +25 -0
- package/templates/dashboard/src/app/page.tsx +154 -0
- package/templates/dashboard/src/app/settings/layout.tsx +22 -0
- package/templates/dashboard/src/app/settings/page.tsx +85 -0
- package/templates/dashboard/src/app/sign-in/page.tsx +47 -0
- package/templates/dashboard/src/app/sign-up/page.tsx +47 -0
- package/templates/dashboard/src/components/auth/email-auth-form.test.tsx +39 -0
- package/templates/dashboard/src/components/auth/email-auth-form.tsx +160 -0
- package/templates/dashboard/src/components/auth/sign-out-button.tsx +29 -0
- package/templates/dashboard/src/components/dashboard/shell.tsx +158 -0
- package/templates/dashboard/src/db/index.ts +16 -0
- package/templates/dashboard/src/db/schema/auth.ts +4 -0
- package/templates/dashboard/src/db/schema/index.ts +2 -0
- package/templates/dashboard/src/db/schema/projects.ts +17 -0
- package/templates/dashboard/src/db/seeds/index.ts +28 -0
- package/templates/dashboard/src/lib/auth-client.ts +5 -0
- package/templates/dashboard/src/lib/auth-session.ts +11 -0
- package/templates/dashboard/src/lib/auth.ts +41 -0
- package/templates/dashboard/src/lib/billing/index.ts +37 -0
- package/templates/dashboard/src/lib/billing/providers/polar.ts +80 -0
- package/templates/dashboard/src/lib/billing/providers/stripe.ts +77 -0
- package/templates/dashboard/src/lib/billing/types.ts +25 -0
- package/templates/dashboard/src/lib/email/index.ts +19 -0
- package/templates/dashboard/src/lib/email/templates.test.ts +15 -0
- package/templates/dashboard/src/lib/email/templates.ts +40 -0
- package/templates/dashboard/src/lib/env.ts +88 -0
- package/templates/dashboard/tsconfig.json +34 -0
- package/templates/dashboard/vitest.config.ts +19 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { redirect } from "next/navigation";
|
|
2
|
+
|
|
3
|
+
import { getSession } from "@/lib/auth-session";
|
|
4
|
+
|
|
5
|
+
export default async function SettingsPage() {
|
|
6
|
+
const session = await getSession();
|
|
7
|
+
|
|
8
|
+
if (!session) {
|
|
9
|
+
redirect("/sign-in");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<main className="dashboard">
|
|
14
|
+
<aside className="sidebar">
|
|
15
|
+
<div>
|
|
16
|
+
<p className="brand">Skit</p>
|
|
17
|
+
<h1 className="workspace-title">Settings</h1>
|
|
18
|
+
<p className="workspace-subtitle">
|
|
19
|
+
Manage your account, notifications, and workspace preferences.
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div className="sidebar-section">
|
|
24
|
+
<p className="sidebar-label">Signed in as</p>
|
|
25
|
+
<div className="pill">
|
|
26
|
+
<span className="status-dot" />
|
|
27
|
+
{session.user.email}
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div className="sidebar-spacer" />
|
|
32
|
+
</aside>
|
|
33
|
+
|
|
34
|
+
<section className="content">
|
|
35
|
+
<div className="settings-grid">
|
|
36
|
+
<article className="settings-card">
|
|
37
|
+
<p className="eyebrow">Profile</p>
|
|
38
|
+
<h2>Identity and session</h2>
|
|
39
|
+
<p>
|
|
40
|
+
Better Auth is wired in. Add avatar upload, password rotation, and audit
|
|
41
|
+
log surfaces here without changing the route structure later.
|
|
42
|
+
</p>
|
|
43
|
+
</article>
|
|
44
|
+
|
|
45
|
+
<article className="settings-card">
|
|
46
|
+
<p className="eyebrow">Billing</p>
|
|
47
|
+
<h2>Plan and seat management</h2>
|
|
48
|
+
<p>
|
|
49
|
+
This is the natural place for subscription state, invoices, and workspace
|
|
50
|
+
access management once billing moves past the starter baseline.
|
|
51
|
+
</p>
|
|
52
|
+
</article>
|
|
53
|
+
|
|
54
|
+
<article className="settings-card settings-card-wide">
|
|
55
|
+
<p className="eyebrow">Ops note</p>
|
|
56
|
+
<h2>Keep settings boring and reliable</h2>
|
|
57
|
+
<p>
|
|
58
|
+
The page is intentionally structured as stable product infrastructure rather
|
|
59
|
+
than visual filler. Add account, organization, and compliance controls here
|
|
60
|
+
as real features land.
|
|
61
|
+
</p>
|
|
62
|
+
</article>
|
|
63
|
+
</div>
|
|
64
|
+
</section>
|
|
65
|
+
</main>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dashboard-shell",
|
|
3
|
+
"kind": "ui",
|
|
4
|
+
"description": "Dashboard-oriented app shell and example UI.",
|
|
5
|
+
"dependsOn": ["auth-core", "testing-baseline"],
|
|
6
|
+
"conflictsWith": [],
|
|
7
|
+
"replaces": [],
|
|
8
|
+
"template": {
|
|
9
|
+
"copyFiles": ["src/app/globals.css", "src/app/settings/page.tsx"]
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "db-pg",
|
|
3
|
+
"kind": "database",
|
|
4
|
+
"description": "Drizzle ORM with node-postgres.",
|
|
5
|
+
"dependsOn": ["quality-baseline"],
|
|
6
|
+
"conflictsWith": ["db-postgresjs"],
|
|
7
|
+
"replaces": [],
|
|
8
|
+
"template": {
|
|
9
|
+
"tokenReplacements": {
|
|
10
|
+
"__DATABASE_DRIVER__": "pg",
|
|
11
|
+
"__DATABASE_DEPENDENCIES__": " \"pg\": \"8.16.x\",",
|
|
12
|
+
"__DATABASE_DEV_DEPENDENCIES__": " \"@types/pg\": \"^8.15.5\",",
|
|
13
|
+
"__DRIZZLE_DRIVER_IMPORT__": "drizzle-orm/node-postgres",
|
|
14
|
+
"__DATABASE_CLIENT_IMPORT__": "import { Pool } from \"pg\";",
|
|
15
|
+
"__DATABASE_GLOBAL_BLOCK__": "const globalForDb = globalThis as typeof globalThis & {\n __pandaPool?: Pool;\n};",
|
|
16
|
+
"__DATABASE_CLIENT_EXPRESSION__": "globalForDb.__pandaPool ??\n new Pool({\n connectionString: env.DATABASE_URL\n })",
|
|
17
|
+
"__DATABASE_DEV_CACHE__": " globalForDb.__pandaPool = client;",
|
|
18
|
+
"__DATABASE_DRIZZLE_EXPRESSION__": "drizzle({\n client\n})"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "db-postgresjs",
|
|
3
|
+
"kind": "database",
|
|
4
|
+
"description": "Drizzle ORM with postgres.js.",
|
|
5
|
+
"dependsOn": ["quality-baseline"],
|
|
6
|
+
"conflictsWith": ["db-pg"],
|
|
7
|
+
"replaces": [],
|
|
8
|
+
"template": {
|
|
9
|
+
"tokenReplacements": {
|
|
10
|
+
"__DATABASE_DRIVER__": "postgres.js",
|
|
11
|
+
"__DATABASE_DEPENDENCIES__": " \"postgres\": \"^3.4.7\",",
|
|
12
|
+
"__DATABASE_DEV_DEPENDENCIES__": "",
|
|
13
|
+
"__DRIZZLE_DRIVER_IMPORT__": "drizzle-orm/postgres-js",
|
|
14
|
+
"__DATABASE_CLIENT_IMPORT__": "import postgres from \"postgres\";",
|
|
15
|
+
"__DATABASE_GLOBAL_BLOCK__": "const globalForDb = globalThis as typeof globalThis & {\n __pandaClient?: ReturnType<typeof postgres>;\n};",
|
|
16
|
+
"__DATABASE_CLIENT_EXPRESSION__": "globalForDb.__pandaClient ??\n postgres(env.DATABASE_URL, {\n prepare: false\n })",
|
|
17
|
+
"__DATABASE_DEV_CACHE__": " globalForDb.__pandaClient = client;",
|
|
18
|
+
"__DATABASE_DRIZZLE_EXPRESSION__": "drizzle(client)"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.git
|
|
2
|
+
.github
|
|
3
|
+
.husky
|
|
4
|
+
.next
|
|
5
|
+
.turbo
|
|
6
|
+
.tmp
|
|
7
|
+
coverage
|
|
8
|
+
node_modules
|
|
9
|
+
npm-debug.log*
|
|
10
|
+
pnpm-debug.log*
|
|
11
|
+
yarn-debug.log*
|
|
12
|
+
yarn-error.log*
|
|
13
|
+
playwright-report
|
|
14
|
+
test-results
|
|
15
|
+
.env
|
|
16
|
+
.env.local
|
|
17
|
+
.env.development.local
|
|
18
|
+
.env.test.local
|
|
19
|
+
.env.production.local
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
FROM __DOCKER_BASE_IMAGE__ AS deps
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
COPY package.json ./
|
|
4
|
+
COPY tsconfig.json ./
|
|
5
|
+
COPY next.config.ts ./
|
|
6
|
+
COPY eslint.config.mjs ./
|
|
7
|
+
COPY prettier.config.mjs ./
|
|
8
|
+
COPY playwright.config.ts ./
|
|
9
|
+
COPY vitest.config.ts ./
|
|
10
|
+
COPY drizzle.config.ts ./
|
|
11
|
+
COPY . .
|
|
12
|
+
__DOCKER_SETUP__
|
|
13
|
+
RUN __DOCKER_INSTALL_COMMAND__
|
|
14
|
+
|
|
15
|
+
FROM deps AS builder
|
|
16
|
+
WORKDIR /app
|
|
17
|
+
RUN __DOCKER_BUILD_COMMAND__
|
|
18
|
+
|
|
19
|
+
FROM __DOCKER_BASE_IMAGE__ AS runner
|
|
20
|
+
WORKDIR /app
|
|
21
|
+
ENV NODE_ENV=production
|
|
22
|
+
COPY --from=builder /app ./
|
|
23
|
+
__DOCKER_SETUP__
|
|
24
|
+
EXPOSE 3000
|
|
25
|
+
CMD ["/bin/sh", "-c", "__DOCKER_START_COMMAND__"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "deploy-docker",
|
|
3
|
+
"kind": "deploy",
|
|
4
|
+
"description": "Docker deployment baseline with Dockerfile and .dockerignore.",
|
|
5
|
+
"dependsOn": ["quality-baseline"],
|
|
6
|
+
"conflictsWith": [],
|
|
7
|
+
"replaces": [],
|
|
8
|
+
"template": {
|
|
9
|
+
"copyFiles": ["Dockerfile", ".dockerignore"]
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "email-resend",
|
|
3
|
+
"kind": "email",
|
|
4
|
+
"description": "Resend transactional email integration.",
|
|
5
|
+
"dependsOn": ["auth-core"],
|
|
6
|
+
"conflictsWith": [],
|
|
7
|
+
"replaces": [],
|
|
8
|
+
"template": {
|
|
9
|
+
"emailProvider": "resend",
|
|
10
|
+
"packageDependencies": [
|
|
11
|
+
" \"resend\": \"6.0.3\","
|
|
12
|
+
],
|
|
13
|
+
"email": {
|
|
14
|
+
"imports": [
|
|
15
|
+
"import { Resend } from \"resend\";"
|
|
16
|
+
],
|
|
17
|
+
"setup": "let resendClient: Resend | null = null;\n\nfunction getEmailClient() {\n if (!env.RESEND_API_KEY) {\n throw new Error(\"RESEND_API_KEY is required for email sending.\");\n }\n\n resendClient ??= new Resend(env.RESEND_API_KEY);\n return resendClient;\n}",
|
|
18
|
+
"sendImplementation": " const resend = getEmailClient();\n\n return resend.emails.send({\n from: getFromAddress(),\n to: input.to,\n subject: input.template.subject,\n text: input.template.text,\n html: input.template.html\n });"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-skit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Opinionated SaaS starter CLI for Next.js 16.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-skit": "./bin/create-skit.mjs"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"lib",
|
|
13
|
+
"modules",
|
|
14
|
+
"presets",
|
|
15
|
+
"templates",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"nextjs",
|
|
20
|
+
"starter",
|
|
21
|
+
"saas",
|
|
22
|
+
"cli",
|
|
23
|
+
"scaffold"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/jocage/skit.git",
|
|
28
|
+
"directory": "apps/cli"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/jocage/skit",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/jocage/skit/issues"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"prepack": "node ../../scripts/sync-cli-package-assets.mjs"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Presets
|
|
2
|
+
|
|
3
|
+
Presets are curated bundles of modules.
|
|
4
|
+
|
|
5
|
+
Current presets:
|
|
6
|
+
|
|
7
|
+
- `blank`
|
|
8
|
+
- `dashboard`
|
|
9
|
+
|
|
10
|
+
For now, the CLI still copies the matching verified template, but preset resolution already determines the module manifest written into generated apps.
|
|
11
|
+
|
|
12
|
+
Each preset now also tracks its `overrideFiles` relative to `templates/base-web`, which makes the shared-vs-specific boundary explicit before full preset assembly is introduced.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "blank",
|
|
3
|
+
"base": "base-web",
|
|
4
|
+
"template": "blank",
|
|
5
|
+
"tokenReplacements": {
|
|
6
|
+
"__TEMPLATE_NAME__": "blank",
|
|
7
|
+
"__AUTH_PROVIDER_ENV_EXAMPLE__": "",
|
|
8
|
+
"__AUTH_PROVIDER_ENV_SCHEMA__": "",
|
|
9
|
+
"__AUTH_PROVIDER_ENV_VALUES__": "",
|
|
10
|
+
"__AUTH_SOCIAL_PROVIDERS_BLOCK__": "",
|
|
11
|
+
"__AUTH_SOCIAL_BUTTON_HANDLER__": "",
|
|
12
|
+
"__AUTH_SOCIAL_BUTTON__": "",
|
|
13
|
+
"__AUTH_SOCIAL_TEST_ASSERTION__": "",
|
|
14
|
+
"__AUTH_SCREEN_CLASS__": "shell auth-shell",
|
|
15
|
+
"__AUTH_PANEL_CLASS__": "auth-card",
|
|
16
|
+
"__SIGN_OUT_BUTTON_CLASS__": "subtle-button",
|
|
17
|
+
"__AUTH_FORM_CLASS__": "auth-card",
|
|
18
|
+
"__AUTH_FORM_COPY_CLASS__": "auth-copy",
|
|
19
|
+
"__AUTH_FORM_FIELDS_CLASS__": "auth-fields",
|
|
20
|
+
"__AUTH_FORM_SIGN_IN_EYEBROW__": "Welcome back",
|
|
21
|
+
"__AUTH_FORM_SIGN_IN_HEADING__": "Sign in to your workspace",
|
|
22
|
+
"__AUTH_FORM_SIGN_IN_LEDE__": "Use your email and password to continue into the starter app shell.",
|
|
23
|
+
"__AUTH_FORM_SIGN_UP_HEADING__": "Create your account",
|
|
24
|
+
"__AUTH_FORM_SIGN_UP_LEDE__": "Start from a minimal SaaS baseline with auth, billing, and strong project structure.",
|
|
25
|
+
"__AUTH_FORM_TEST_MODE__": "sign-in",
|
|
26
|
+
"__AUTH_FORM_TEST_HEADING__": "sign in to your workspace",
|
|
27
|
+
"__AUTH_FORM_TEST_LABEL__": "email",
|
|
28
|
+
"__EMAIL_TEMPLATE_TEST_IMPORT__": "createWelcomeEmailTemplate",
|
|
29
|
+
"__EMAIL_TEMPLATE_TEST_SUITE__": "createWelcomeEmailTemplate",
|
|
30
|
+
"__EMAIL_TEMPLATE_TEST_CASE__": "includes the app name in the subject",
|
|
31
|
+
"__EMAIL_TEMPLATE_TEST_FACTORY__": "createWelcomeEmailTemplate({\n appName: \"Skit\",\n recipientName: \"Ada\"\n })",
|
|
32
|
+
"__EMAIL_TEMPLATE_EXPECT_SUBJECT__": "Skit",
|
|
33
|
+
"__EMAIL_TEMPLATE_EXPECT_TEXT__": "Ada",
|
|
34
|
+
"__PROTECTED_ROUTE_CONDITION__": "pathname.startsWith(\"/dashboard\") ||\n pathname.startsWith(\"/billing\") ||\n pathname.startsWith(\"/email\")",
|
|
35
|
+
"__PROTECTED_ROUTE_MATCHERS__": "\"/dashboard/:path*\", \"/billing/:path*\", \"/email/:path*\"",
|
|
36
|
+
"__README_STATE_COPY__": "initial minimal shell for the starter",
|
|
37
|
+
"__README_HIGHLIGHT_BULLET__": "simple polished landing screen",
|
|
38
|
+
"__README_PROTECTED_ROUTES_BULLET__": "protected `/dashboard` route and auth handler",
|
|
39
|
+
"__HOME_MAIN_CLASS__": "shell",
|
|
40
|
+
"__HOME_SECTION_CLASS__": "hero",
|
|
41
|
+
"__HOME_HEADING__": "Your SaaS starts here.",
|
|
42
|
+
"__HOME_LEDE__": "Auth, database, billing, email — pre-wired and ready. Extend the blank baseline with real product features instead of rebuilding infrastructure.",
|
|
43
|
+
"__HOME_PAGE_CONTENT__": "(\n <>\n <header className=\"blank-header\">\n <span className=\"blank-logo\">__PROJECT_NAME__</span>\n <nav className=\"blank-nav\">\n <Link href=\"/sign-in\">Sign in</Link>\n <Link href=\"/sign-up\" className=\"blank-nav-cta\">Get started</Link>\n </nav>\n </header>\n <main className=\"shell\">\n <section className=\"hero\">\n <p className=\"eyebrow\">Skit</p>\n <h1>Your SaaS starts here.</h1>\n <p className=\"lede\">\n Auth, database, billing, email — pre-wired and ready.\n Extend the blank baseline with real product features.\n </p>\n <div className=\"hero-actions\">\n <Link href=\"/sign-up\">Create account</Link>\n <Link href=\"/dashboard\">Open dashboard</Link>\n </div>\n </section>\n </main>\n </>\n )",
|
|
44
|
+
"__BILLING_REDIRECT_IMPORT__": "import { redirect } from \"next/navigation\";\n",
|
|
45
|
+
"__BILLING_SESSION_SETUP__": "",
|
|
46
|
+
"__BILLING_AUTH_GUARD__": " if (!session) {\n redirect(\"/sign-in\");\n }",
|
|
47
|
+
"__BILLING_DEMO_BANNER__": "",
|
|
48
|
+
"__BILLING_PROVIDER_CARD__": " <form\n key={provider}\n action=\"/api/billing/checkout\"\n method=\"post\"\n className=\"billing-card\"\n >\n <input type=\"hidden\" name=\"provider\" value={provider} />\n <h2>{provider === \"stripe\" ? \"Stripe\" : \"Polar\"}</h2>\n <p>\n {provider === \"stripe\"\n ? \"Create a subscription checkout session using the configured Stripe price.\"\n : \"Create a Polar checkout session using the configured product identifier.\"}\n </p>\n <button type=\"submit\" className=\"auth-button\">\n {provider === \"stripe\" ? \"Start Stripe checkout\" : \"Start Polar checkout\"}\n </button>\n </form>",
|
|
49
|
+
"__EMAIL_REDIRECT_IMPORT__": "import { redirect } from \"next/navigation\";\n",
|
|
50
|
+
"__EMAIL_SESSION_SETUP__": "",
|
|
51
|
+
"__EMAIL_AUTH_GUARD__": " if (!session) {\n redirect(\"/sign-in\");\n }\n\n const displayEmailRecipient = session.user.email;\n const emailSendingDisabled = false;",
|
|
52
|
+
"__EMAIL_DEMO_BANNER__": "",
|
|
53
|
+
"__DASHBOARD_REDIRECT_IMPORT__": "import { redirect } from \"next/navigation\";\n",
|
|
54
|
+
"__DASHBOARD_SESSION_SETUP__": "",
|
|
55
|
+
"__DASHBOARD_AUTH_GUARD__": " if (!session) {\n redirect(\"/sign-in\");\n }",
|
|
56
|
+
"__DASHBOARD_PAGE_PRELUDE__": "",
|
|
57
|
+
"__DASHBOARD_PAGE_CONTENT__": "(\n <main className=\"shell\">\n <section className=\"hero\">\n <p className=\"eyebrow\">Protected area</p>\n <h1>Hello {session.user.name ?? session.user.email}</h1>\n <p className=\"lede\">\n This is the first protected route in the blank starter. It proves the auth\n flow, route guarding, and session wiring are in place.\n </p>\n <div className=\"hero-actions\">\n <Link href=\"/\">Go home</Link>\n <Link href=\"/billing\">Billing</Link>\n <Link href=\"/email\">Email</Link>\n <SignOutButton />\n </div>\n </section>\n </main>\n )",
|
|
58
|
+
"__HOME_E2E_TEST_NAME__": "home page renders starter links",
|
|
59
|
+
"__HOME_E2E_HEADING__": "your saas starts here",
|
|
60
|
+
"__HOME_E2E_LINK__": "create account",
|
|
61
|
+
"__AUTH_LLMS_EXTRA__": "",
|
|
62
|
+
"__BILLING_LLMS_LINE__": "Stripe / Polar billing (provider abstraction)",
|
|
63
|
+
"__EMAIL_LLMS_LINE__": "Resend transactional email"
|
|
64
|
+
},
|
|
65
|
+
"overrideFiles": [],
|
|
66
|
+
"modules": ["quality-baseline", "testing-baseline", "auth-core"]
|
|
67
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dashboard",
|
|
3
|
+
"base": "base-web",
|
|
4
|
+
"template": "dashboard",
|
|
5
|
+
"tokenReplacements": {
|
|
6
|
+
"__TEMPLATE_NAME__": "dashboard",
|
|
7
|
+
"__AUTH_PROVIDER_ENV_EXAMPLE__": "",
|
|
8
|
+
"__AUTH_PROVIDER_ENV_SCHEMA__": "",
|
|
9
|
+
"__AUTH_PROVIDER_ENV_VALUES__": "",
|
|
10
|
+
"__AUTH_SOCIAL_PROVIDERS_BLOCK__": "",
|
|
11
|
+
"__AUTH_SOCIAL_BUTTON_HANDLER__": "",
|
|
12
|
+
"__AUTH_SOCIAL_BUTTON__": "",
|
|
13
|
+
"__AUTH_SOCIAL_TEST_ASSERTION__": "",
|
|
14
|
+
"__AUTH_SCREEN_CLASS__": "auth-screen",
|
|
15
|
+
"__AUTH_PANEL_CLASS__": "auth-panel",
|
|
16
|
+
"__SIGN_OUT_BUTTON_CLASS__": "ghost-button",
|
|
17
|
+
"__AUTH_FORM_CLASS__": "auth-panel",
|
|
18
|
+
"__AUTH_FORM_COPY_CLASS__": "",
|
|
19
|
+
"__AUTH_FORM_FIELDS_CLASS__": "auth-grid",
|
|
20
|
+
"__AUTH_FORM_SIGN_IN_EYEBROW__": "Access dashboard",
|
|
21
|
+
"__AUTH_FORM_SIGN_IN_HEADING__": "Sign in",
|
|
22
|
+
"__AUTH_FORM_SIGN_IN_LEDE__": "Use your email and password to continue into the app.",
|
|
23
|
+
"__AUTH_FORM_SIGN_UP_HEADING__": "Create your operator account",
|
|
24
|
+
"__AUTH_FORM_SIGN_UP_LEDE__": "Create an account to access the protected dashboard shell.",
|
|
25
|
+
"__AUTH_FORM_TEST_MODE__": "sign-up",
|
|
26
|
+
"__AUTH_FORM_TEST_HEADING__": "create your operator account",
|
|
27
|
+
"__AUTH_FORM_TEST_LABEL__": "password",
|
|
28
|
+
"__EMAIL_TEMPLATE_TEST_IMPORT__": "createBillingUpdatePlaceholderTemplate",
|
|
29
|
+
"__EMAIL_TEMPLATE_TEST_SUITE__": "createBillingUpdatePlaceholderTemplate",
|
|
30
|
+
"__EMAIL_TEMPLATE_TEST_CASE__": "mentions the billing provider",
|
|
31
|
+
"__EMAIL_TEMPLATE_TEST_FACTORY__": "createBillingUpdatePlaceholderTemplate({\n appName: \"Skit\",\n provider: \"stripe\"\n })",
|
|
32
|
+
"__EMAIL_TEMPLATE_EXPECT_SUBJECT__": "billing",
|
|
33
|
+
"__EMAIL_TEMPLATE_EXPECT_TEXT__": "stripe",
|
|
34
|
+
"__PROTECTED_ROUTE_CONDITION__": "pathname.startsWith(\"/settings\") ||\n pathname.startsWith(\"/billing\") ||\n pathname.startsWith(\"/email\")",
|
|
35
|
+
"__PROTECTED_ROUTE_MATCHERS__": "\"/settings/:path*\", \"/billing/:path*\", \"/email/:path*\"",
|
|
36
|
+
"__README_STATE_COPY__": "initial dashboard-oriented shell for the starter",
|
|
37
|
+
"__README_HIGHLIGHT_BULLET__": "modern SaaS marketing page plus public demo dashboard shell",
|
|
38
|
+
"__README_PROTECTED_ROUTES_BULLET__": "public demo `/dashboard` plus protected account surfaces like `/settings`, `/billing`, and `/email`",
|
|
39
|
+
"__HOME_MAIN_CLASS__": "landing",
|
|
40
|
+
"__HOME_SECTION_CLASS__": "landing-panel",
|
|
41
|
+
"__HOME_HEADING__": "Launch faster with a modular SaaS base.",
|
|
42
|
+
"__HOME_LEDE__": "This template now ships with a public demo dashboard, Better Auth wiring, database scaffolding, and the first product surfaces already in place.",
|
|
43
|
+
"__HOME_PAGE_CONTENT__": "(\n <main className=\"landing-page\">\n <nav className=\"marketing-nav\">\n <div className=\"logo\">launch<em>frame</em></div>\n <div className=\"marketing-nav-links\">\n <Link href=\"/sign-in\">Sign in</Link>\n <Link href=\"/sign-up\" className=\"nav-cta\">Get started</Link>\n </div>\n </nav>\n\n <div className=\"marketing-shell\">\n <section className=\"marketing-hero\">\n <span className=\"badge-pill\">Ship faster in 2026</span>\n <h1>Build your SaaS <em>without the boilerplate.</em></h1>\n <p className=\"lede\">\n Auth, billing, email, database — all wired up and ready.\n Stop rebuilding infrastructure and start shipping your product.\n </p>\n <div className=\"hero-actions\">\n <Link href=\"/dashboard\">Explore the live demo</Link>\n <Link href=\"/sign-up\">Start from this baseline</Link>\n <Link href=\"/sign-in\">Sign in</Link>\n </div>\n </section>\n\n <section className=\"bento-section\">\n <p className=\"eyebrow\">Everything included</p>\n <h2>Production-ready from day one</h2>\n <div className=\"bento-grid\">\n <div className=\"bento-card span-2\">\n <span className=\"bento-icon\">🔐</span>\n <h3>Authentication</h3>\n <p>Email/password auth with Better Auth. Session management, protected routes, and middleware — all pre-configured.</p>\n <div className=\"bento-tags\"><span className=\"bento-tag\">Better Auth</span><span className=\"bento-tag\">Server sessions</span><span className=\"bento-tag\">Edge middleware</span></div>\n </div>\n <div className=\"bento-card\">\n <span className=\"bento-icon\">💳</span>\n <h3>Billing</h3>\n <p>Stripe and Polar checkout flows with a provider abstraction you can extend.</p>\n <div className=\"bento-tags\"><span className=\"bento-tag\">Stripe</span><span className=\"bento-tag\">Polar</span></div>\n </div>\n <div className=\"bento-card\">\n <span className=\"bento-icon\">🗄️</span>\n <h3>Database</h3>\n <p>Drizzle ORM with migrations, seeds, and a typed schema ready for your models.</p>\n <div className=\"bento-tags\"><span className=\"bento-tag\">Drizzle</span><span className=\"bento-tag\">PostgreSQL</span></div>\n </div>\n <div className=\"bento-card span-2\">\n <span className=\"bento-icon\">📧</span>\n <h3>Transactional email</h3>\n <p>Resend integration with HTML templates for welcome emails, password resets, and billing notifications.</p>\n <div className=\"bento-tags\"><span className=\"bento-tag\">Resend</span><span className=\"bento-tag\">HTML templates</span></div>\n </div>\n <div className=\"bento-card\">\n <span className=\"bento-icon\">🧪</span>\n <h3>Testing</h3>\n <p>Vitest for unit tests, Playwright for E2E. Pre-commit hooks enforce quality.</p>\n <div className=\"bento-tags\"><span className=\"bento-tag\">Vitest</span><span className=\"bento-tag\">Playwright</span></div>\n </div>\n <div className=\"bento-card\">\n <span className=\"bento-icon\">⚡</span>\n <h3>Next.js 16</h3>\n <p>App Router, React 19, server components, and the latest compiler out of the box.</p>\n <div className=\"bento-tags\"><span className=\"bento-tag\">App Router</span><span className=\"bento-tag\">React 19</span></div>\n </div>\n <div className=\"bento-card\">\n <span className=\"bento-icon\">🤖</span>\n <h3>Agent-friendly</h3>\n <p>AGENTS.md, explicit placeholders, and a flat file tree that AI coding assistants love.</p>\n <div className=\"bento-tags\"><span className=\"bento-tag\">Cursor</span><span className=\"bento-tag\">Copilot</span></div>\n </div>\n </div>\n </section>\n\n <section className=\"pricing-section\">\n <p className=\"eyebrow\">Pricing</p>\n <h2>Simple, transparent pricing</h2>\n <p className=\"lede\">Start free, scale as you grow. No hidden fees, cancel anytime.</p>\n <div className=\"pricing-grid\">\n <div className=\"pricing-card\">\n <p className=\"plan-name\">Starter</p>\n <div className=\"plan-price\"><strong>$0</strong><span>/month</span></div>\n <p className=\"plan-desc\">For side projects and experiments.</p>\n <ul className=\"pricing-features\">\n <li>Up to 100 users</li>\n <li>Basic analytics</li>\n <li>Community support</li>\n <li>1 project</li>\n </ul>\n <Link href=\"/sign-up\" className=\"pricing-cta\">Get started free</Link>\n </div>\n <div className=\"pricing-card featured\">\n <p className=\"plan-name\">Pro</p>\n <div className=\"plan-price\"><strong>$29</strong><span>/month</span></div>\n <p className=\"plan-desc\">For growing products that need more.</p>\n <ul className=\"pricing-features\">\n <li>Unlimited users</li>\n <li>Advanced analytics</li>\n <li>Priority support</li>\n <li>Unlimited projects</li>\n <li>Custom domain</li>\n </ul>\n <Link href=\"/sign-up\" className=\"pricing-cta\">Start 14-day trial</Link>\n </div>\n <div className=\"pricing-card\">\n <p className=\"plan-name\">Enterprise</p>\n <div className=\"plan-price\"><strong>Custom</strong></div>\n <p className=\"plan-desc\">For teams with specific needs.</p>\n <ul className=\"pricing-features\">\n <li>Everything in Pro</li>\n <li>SSO & SAML</li>\n <li>Dedicated support</li>\n <li>SLA guarantee</li>\n <li>Custom integrations</li>\n </ul>\n <Link href=\"/sign-up\" className=\"pricing-cta\">Contact sales</Link>\n </div>\n </div>\n </section>\n\n <section className=\"cta-section\">\n <h2>Ready to ship?</h2>\n <p>Generate your project in seconds. Everything is yours to customize — no vendor lock-in, no hidden abstractions.</p>\n <div className=\"hero-actions\">\n <Link href=\"/dashboard\">Open live dashboard</Link>\n <Link href=\"/sign-up\">Create account</Link>\n </div>\n </section>\n\n <footer className=\"marketing-footer\">\n <p>Built with Skit — open source SaaS starter for Next.js</p>\n </footer>\n </div>\n </main>\n )",
|
|
44
|
+
"__DASHBOARD_REDIRECT_IMPORT__": "",
|
|
45
|
+
"__DASHBOARD_SESSION_SETUP__": " const displayUser = session?.user ?? {\n email: \"eugene@jocage.com\",\n name: \"Demo Workspace\"\n };",
|
|
46
|
+
"__DASHBOARD_AUTH_GUARD__": "",
|
|
47
|
+
"__BILLING_REDIRECT_IMPORT__": "",
|
|
48
|
+
"__BILLING_SESSION_SETUP__": " const displayBillingUser = session?.user ?? {\n email: \"eugene@jocage.com\",\n name: \"Demo Workspace\"\n };",
|
|
49
|
+
"__BILLING_AUTH_GUARD__": "",
|
|
50
|
+
"__BILLING_DEMO_BANNER__": " {!session ? (\n <p className=\"demo-banner\">\n Demo mode is active. Checkout buttons stay visible for product context, but\n real billing flows should be tested after auth and provider credentials are configured.\n </p>\n ) : null}",
|
|
51
|
+
"__BILLING_PROVIDER_CARD__": " <form\n key={provider}\n action=\"/api/billing/checkout\"\n method=\"post\"\n className=\"billing-card\"\n >\n <input type=\"hidden\" name=\"provider\" value={provider} />\n <h2>{provider === \"stripe\" ? \"Stripe\" : \"Polar\"}</h2>\n <p>\n {provider === \"stripe\"\n ? \"Create a subscription checkout session using the configured Stripe price.\"\n : \"Create a Polar checkout session using the configured product identifier.\"}\n </p>\n <p className=\"billing-caption\">\n Viewing as <strong>{displayBillingUser.name ?? displayBillingUser.email}</strong>\n </p>\n <button type=\"submit\" className=\"auth-button\" disabled={!session}>\n {!session\n ? provider === \"stripe\"\n ? \"Sign in to test Stripe\"\n : \"Sign in to test Polar\"\n : provider === \"stripe\"\n ? \"Start Stripe checkout\"\n : \"Start Polar checkout\"}\n </button>\n </form>",
|
|
52
|
+
"__EMAIL_REDIRECT_IMPORT__": "",
|
|
53
|
+
"__EMAIL_SESSION_SETUP__": " const displayEmailRecipient = session?.user?.email ?? \"eugene@jocage.com\";\n const emailSendingDisabled = !session;",
|
|
54
|
+
"__EMAIL_AUTH_GUARD__": "",
|
|
55
|
+
"__EMAIL_DEMO_BANNER__": " {!session ? (\n <p className=\"demo-banner\">\n Demo mode is active. The email surface stays visible, but actual send actions\n unlock after sign-in and provider configuration.\n </p>\n ) : null}",
|
|
56
|
+
"__DASHBOARD_PAGE_PRELUDE__": "const stats = [\n { label: \"MRR\", value: \"$12.4k\", change: \"+8.2%\", tone: \"positive\" },\n { label: \"Trials\", value: \"184\", change: \"+14.9%\", tone: \"positive\" },\n { label: \"Activation\", value: \"62%\", change: \"+5.1%\", tone: \"positive\" },\n { label: \"Churn\", value: \"2.1%\", change: \"Watchlist\", tone: \"warning\" }\n];\n\nconst activity = [\n {\n title: \"Polar and Stripe adapters are already wired\",\n description: \"Swap providers without rewriting your app surface.\",\n stamp: \"Now\",\n badge: \"BL\"\n },\n {\n title: \"Better Auth baseline is live\",\n description: \"Email auth flows, protected routes, and session helpers are in place.\",\n stamp: \"Auth\",\n badge: \"AU\"\n },\n {\n title: \"Vitest + Playwright gates are included\",\n description: \"You start with verification, not just templates and good intentions.\",\n stamp: \"QA\",\n badge: \"TS\"\n }\n];\n\nconst pipeline = [\n { label: \"Lead capture\", value: \"1,284\", detail: \"+11% week over week\" },\n { label: \"Qualified trials\", value: \"312\", detail: \"42 from referral channels\" },\n { label: \"Activated workspaces\", value: \"116\", detail: \"62% activation rate\" }\n];",
|
|
57
|
+
"__DASHBOARD_PAGE_CONTENT__": "(\n <main className=\"dashboard\">\n <aside className=\"sidebar\">\n <div>\n <p className=\"brand\">Skit</p>\n <h1 className=\"workspace-title\">Control room</h1>\n <p className=\"workspace-subtitle\">\n Start with a product shell that already understands auth, billing, email,\n and deployment paths.\n </p>\n </div>\n\n <div className=\"sidebar-section\">\n <p className=\"sidebar-label\">Workspace</p>\n <div className=\"pill\">\n <span className=\"status-dot\" />\n {displayUser.name ?? displayUser.email}\n </div>\n </div>\n\n <nav className=\"nav\">\n <Link href=\"/dashboard\">\n Overview <span>01</span>\n </Link>\n <Link href=\"/billing\">\n Billing <span>02</span>\n </Link>\n <Link href=\"/email\">\n Email <span>03</span>\n </Link>\n <Link href=\"/settings\">\n Settings <span>04</span>\n </Link>\n <Link href=\"/\">\n Home <span>05</span>\n </Link>\n </nav>\n\n <div className=\"sidebar-note\">\n <strong>Starter principle</strong>\n <p>\n Keep the generated app sharp, predictable, and easy to extend with both\n human and agent workflows.\n </p>\n </div>\n\n {session ? (\n <SignOutButton />\n ) : (\n <Link href=\"/sign-in\" className=\"ghost-button\">\n Sign in for the full app\n </Link>\n )}\n </aside>\n\n <section className=\"content\">\n <header className=\"header\">\n <article className=\"hero-panel\">\n <p className=\"eyebrow\">Dashboard starter</p>\n <h2>Ship the first real screen on day zero.</h2>\n <p className=\"lede\">\n This is not a toy admin mockup. It is the first protected surface of a\n modular SaaS app, with auth, billing, email, and database boundaries\n already mapped out.\n </p>\n <div className=\"hero-actions\">\n <Link href=\"/billing\">Open billing</Link>\n <Link href=\"/settings\">Open settings</Link>\n <Link href=\"/email\">Send a test email</Link>\n </div>\n </article>\n\n <article className=\"insight-panel\">\n <p className=\"eyebrow\">Why it matters</p>\n <h3>Clean foundations beat feature theater.</h3>\n <ul className=\"insight-list\">\n <li>\n <strong>Composable modules</strong>\n <span>DB, billing, deploy target, and dashboard shell are resolved intentionally.</span>\n </li>\n <li>\n <strong>AI-first repo shape</strong>\n <span>Thin routes, clear boundaries, and generated manifests keep agent work predictable.</span>\n </li>\n <li>\n <strong>Verified output</strong>\n <span>Smoke profiles exist so the starter stays real, not aspirational.</span>\n </li>\n </ul>\n </article>\n </header>\n\n <section className=\"stats\">\n {stats.map((item) => (\n <article key={item.label} className=\"card\">\n <p>{item.label}</p>\n <strong>{item.value}</strong>\n <span className={`metric-trend ${item.tone}`}>{item.change}</span>\n </article>\n ))}\n </section>\n\n <section className=\"panel-grid\">\n <article className=\"panel\">\n <p className=\"eyebrow\">Recent activity</p>\n <h3>What this starter already gives you</h3>\n <div className=\"activity-list\">\n {activity.map((item) => (\n <div key={item.title} className=\"activity-item\">\n <div className=\"activity-badge\">{item.badge}</div>\n <div>\n <strong>{item.title}</strong>\n <p>{item.description}</p>\n </div>\n <span>{item.stamp}</span>\n </div>\n ))}\n </div>\n </article>\n\n <aside className=\"mini-grid\">\n <article className=\"mini-card\">\n <p className=\"eyebrow\">Auth</p>\n <strong>Ready when you need real accounts</strong>\n <p>Auth exists, but the demo dashboard no longer blocks first-run product exploration.</p>\n </article>\n <article className=\"mini-card\">\n <p className=\"eyebrow\">Deploy</p>\n <strong>Vercel or Docker</strong>\n <p>The generator keeps deploy target selection explicit instead of burying it in docs.</p>\n </article>\n <article className=\"mini-card\">\n <p className=\"eyebrow\">Billing</p>\n <strong>Provider abstraction first</strong>\n <p>Stripe and Polar stay behind one app-facing contract from the beginning.</p>\n </article>\n </aside>\n </section>\n\n <section className=\"pipeline-panel\">\n <div className=\"pipeline-header\">\n <div>\n <p className=\"eyebrow\">Growth pipeline</p>\n <h3>Demo data that feels like a product, not a lorem ipsum card set.</h3>\n </div>\n <span className=\"pill\">\n <span className=\"status-dot\" />\n Demo mode\n </span>\n </div>\n <div className=\"pipeline-grid\">\n {pipeline.map((item) => (\n <article key={item.label} className=\"pipeline-card\">\n <p>{item.label}</p>\n <strong>{item.value}</strong>\n <span>{item.detail}</span>\n </article>\n ))}\n </div>\n </section>\n </section>\n </main>\n )",
|
|
58
|
+
"__HOME_E2E_TEST_NAME__": "home page renders skit landing",
|
|
59
|
+
"__HOME_E2E_HEADING__": "build your saas",
|
|
60
|
+
"__HOME_E2E_LINK__": "explore the live demo",
|
|
61
|
+
"__AUTH_LLMS_EXTRA__": "",
|
|
62
|
+
"__BILLING_LLMS_LINE__": "Stripe / Polar billing (provider abstraction)",
|
|
63
|
+
"__EMAIL_LLMS_LINE__": "Resend transactional email"
|
|
64
|
+
},
|
|
65
|
+
"overrideFiles": [],
|
|
66
|
+
"modules": ["quality-baseline", "testing-baseline", "auth-core", "dashboard-shell"]
|
|
67
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
|
2
|
+
BETTER_AUTH_URL=http://localhost:3000
|
|
3
|
+
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/__PROJECT_NAME__
|
|
4
|
+
BETTER_AUTH_SECRET=replace-me
|
|
5
|
+
__AUTH_PROVIDER_ENV_EXAMPLE__
|
|
6
|
+
RESEND_API_KEY=re_xxx
|
|
7
|
+
EMAIL_FROM=Panda Starter <onboarding@example.com>
|
|
8
|
+
STRIPE_SECRET_KEY=sk_test_xxx
|
|
9
|
+
STRIPE_PRICE_ID=price_xxx
|
|
10
|
+
STRIPE_WEBHOOK_SECRET=whsec_xxx
|
|
11
|
+
POLAR_ACCESS_TOKEN=polar_pat_xxx
|
|
12
|
+
POLAR_ORGANIZATION_ID=org_xxx
|
|
13
|
+
POLAR_PRODUCT_ID=prod_xxx
|
|
14
|
+
POLAR_SERVER=sandbox
|
|
15
|
+
POLAR_WEBHOOK_SECRET=polar_whsec_xxx
|
|
16
|
+
BILLING_PROVIDER=__BILLING_PROVIDER__
|
|
17
|
+
EMAIL_PROVIDER=__EMAIL_PROVIDER__
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
quality:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout
|
|
13
|
+
uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Setup Node
|
|
16
|
+
uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: 22
|
|
19
|
+
cache: __CI_CACHE__
|
|
20
|
+
|
|
21
|
+
- name: Install
|
|
22
|
+
run: __CI_INSTALL_COMMAND__
|
|
23
|
+
|
|
24
|
+
- name: Lint
|
|
25
|
+
run: __CI_RUN_LINT__
|
|
26
|
+
|
|
27
|
+
- name: Typecheck
|
|
28
|
+
run: __CI_RUN_TYPECHECK__
|
|
29
|
+
|
|
30
|
+
- name: Unit tests
|
|
31
|
+
run: __CI_RUN_TEST__
|
|
32
|
+
|
|
33
|
+
- name: Build
|
|
34
|
+
run: __CI_RUN_BUILD__
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
Generated with Skit using the `__TEMPLATE_NAME__` template.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
__INSTALL_COMMAND__
|
|
9
|
+
__RUN_DEV_COMMAND__
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Generated settings:
|
|
13
|
+
|
|
14
|
+
- package manager: `__PACKAGE_MANAGER_NAME__`
|
|
15
|
+
- database driver: `__DATABASE_DRIVER__`
|
|
16
|
+
- billing provider: `__BILLING_PROVIDER__`
|
|
17
|
+
- email provider: `__EMAIL_PROVIDER__`
|
|
18
|
+
- deploy target: `__DEPLOY_TARGET__`
|
|
19
|
+
- demo seed data: `__SEED_DEMO_DATA__`
|
|
20
|
+
|
|
21
|
+
## Current State
|
|
22
|
+
|
|
23
|
+
This is the __README_STATE_COPY__.
|
|
24
|
+
|
|
25
|
+
Included:
|
|
26
|
+
|
|
27
|
+
- Next.js 16 app shell
|
|
28
|
+
- strict TypeScript baseline
|
|
29
|
+
- __README_HIGHLIGHT_BULLET__
|
|
30
|
+
- flat ESLint config with strict rules
|
|
31
|
+
- Prettier config
|
|
32
|
+
- validated env entrypoint with Zod
|
|
33
|
+
- Drizzle ORM baseline with PostgreSQL via `__DATABASE_DRIVER__`
|
|
34
|
+
- migration and seed scripts
|
|
35
|
+
- Better Auth baseline with email/password sign-in and sign-up
|
|
36
|
+
- __README_PROTECTED_ROUTES_BULLET__
|
|
37
|
+
- billing provider abstraction with `__BILLING_PROVIDER__` as the configured default
|
|
38
|
+
- billing page and webhook route skeletons
|
|
39
|
+
- `__EMAIL_PROVIDER__` email service layer and email template placeholders
|
|
40
|
+
- protected `/email` page for test sends
|
|
41
|
+
- Vitest and Playwright baseline with example tests
|
|
42
|
+
- GitHub Actions CI workflow for lint, typecheck, test, and build
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from "drizzle-kit";
|
|
2
|
+
|
|
3
|
+
if (!process.env.DATABASE_URL) {
|
|
4
|
+
throw new Error("DATABASE_URL is required to use Drizzle.");
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
schema: "./src/db/schema/index.ts",
|
|
9
|
+
out: "./src/db/migrations",
|
|
10
|
+
dialect: "postgresql",
|
|
11
|
+
dbCredentials: {
|
|
12
|
+
url: process.env.DATABASE_URL
|
|
13
|
+
},
|
|
14
|
+
verbose: true,
|
|
15
|
+
strict: true
|
|
16
|
+
});
|