@techstream/quark-create-app 1.9.0 → 1.10.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.
Files changed (74) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/src/index.js +376 -143
  4. package/templates/base-project/.cursor/rules/quark.mdc +172 -0
  5. package/templates/base-project/.github/copilot-instructions.md +55 -0
  6. package/templates/base-project/CLAUDE.md +273 -0
  7. package/templates/base-project/README.md +72 -30
  8. package/templates/base-project/apps/web/next.config.js +5 -1
  9. package/templates/base-project/apps/web/package.json +3 -3
  10. package/templates/base-project/apps/web/public/quark.svg +46 -0
  11. package/templates/base-project/apps/web/railway.json +2 -2
  12. package/templates/base-project/apps/web/src/app/_components/HealthIndicator.js +85 -0
  13. package/templates/base-project/apps/web/src/app/_components/HomeThemeToggle.js +63 -0
  14. package/templates/base-project/apps/web/src/app/_components/QuarkAnimation.js +168 -0
  15. package/templates/base-project/apps/web/src/app/api/health/route.js +28 -17
  16. package/templates/base-project/apps/web/src/app/favicon.ico +0 -0
  17. package/templates/base-project/apps/web/src/app/global-error.js +53 -0
  18. package/templates/base-project/apps/web/src/app/globals.css +121 -15
  19. package/templates/base-project/apps/web/src/app/icon.svg +46 -0
  20. package/templates/base-project/apps/web/src/app/layout.js +1 -0
  21. package/templates/base-project/apps/web/src/app/not-found.js +35 -0
  22. package/templates/base-project/apps/web/src/app/page.js +38 -5
  23. package/templates/base-project/apps/web/src/lib/theme.js +23 -0
  24. package/templates/base-project/apps/web/src/proxy.js +10 -2
  25. package/templates/base-project/package.json +2 -0
  26. package/templates/base-project/packages/db/src/client.js +6 -1
  27. package/templates/base-project/packages/db/src/index.js +1 -0
  28. package/templates/base-project/packages/db/src/ping.js +66 -0
  29. package/templates/base-project/scripts/doctor.js +261 -0
  30. package/templates/base-project/turbo.json +2 -1
  31. package/templates/config/package.json +1 -0
  32. package/templates/jobs/package.json +2 -1
  33. package/templates/ui/README.md +67 -0
  34. package/templates/ui/package.json +1 -0
  35. package/templates/ui/src/badge.js +32 -0
  36. package/templates/ui/src/badge.test.js +42 -0
  37. package/templates/ui/src/button.js +64 -15
  38. package/templates/ui/src/button.test.js +34 -5
  39. package/templates/ui/src/card.js +58 -0
  40. package/templates/ui/src/card.test.js +59 -0
  41. package/templates/ui/src/checkbox.js +35 -0
  42. package/templates/ui/src/checkbox.test.js +35 -0
  43. package/templates/ui/src/dialog.js +139 -0
  44. package/templates/ui/src/dialog.test.js +15 -0
  45. package/templates/ui/src/index.js +16 -0
  46. package/templates/ui/src/input.js +15 -0
  47. package/templates/ui/src/input.test.js +27 -0
  48. package/templates/ui/src/label.js +14 -0
  49. package/templates/ui/src/label.test.js +22 -0
  50. package/templates/ui/src/select.js +42 -0
  51. package/templates/ui/src/select.test.js +27 -0
  52. package/templates/ui/src/skeleton.js +14 -0
  53. package/templates/ui/src/skeleton.test.js +22 -0
  54. package/templates/ui/src/table.js +75 -0
  55. package/templates/ui/src/table.test.js +69 -0
  56. package/templates/ui/src/textarea.js +15 -0
  57. package/templates/ui/src/textarea.test.js +27 -0
  58. package/templates/ui/src/theme-constants.js +24 -0
  59. package/templates/ui/src/theme.js +132 -0
  60. package/templates/ui/src/toast.js +229 -0
  61. package/templates/ui/src/toast.test.js +23 -0
  62. package/templates/{base-project/apps/worker → worker}/package.json +2 -2
  63. package/templates/{base-project/apps/worker → worker}/src/index.js +38 -23
  64. package/templates/{base-project/apps/worker → worker}/src/index.test.js +19 -20
  65. package/templates/base-project/apps/web/public/file.svg +0 -1
  66. package/templates/base-project/apps/web/public/globe.svg +0 -1
  67. package/templates/base-project/apps/web/public/next.svg +0 -1
  68. package/templates/base-project/apps/web/public/vercel.svg +0 -1
  69. package/templates/base-project/apps/web/public/window.svg +0 -1
  70. /package/templates/{base-project/apps/worker → worker}/README.md +0 -0
  71. /package/templates/{base-project/apps/worker → worker}/railway.json +0 -0
  72. /package/templates/{base-project/apps/worker → worker}/src/handlers/email.js +0 -0
  73. /package/templates/{base-project/apps/worker → worker}/src/handlers/files.js +0 -0
  74. /package/templates/{base-project/apps/worker → worker}/src/handlers/index.js +0 -0
@@ -0,0 +1,172 @@
1
+ ---
2
+ description: Quark project conventions for __QUARK_PROJECT_NAME__. Covers all layers: UI, backend, database, auth, jobs, testing, and deployment.
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Quark Project: __QUARK_PROJECT_NAME__
7
+
8
+ Package scope: `@__QUARK_SCOPE__` — use this for ALL workspace packages.
9
+ Scaffolded: __QUARK_SCAFFOLD_DATE__
10
+
11
+ ---
12
+
13
+ ## Non-Negotiable Rules
14
+
15
+ 1. **ESM only.** Always `import`/`export`. Never `require()` or `module.exports`.
16
+ 2. **No TypeScript.** Plain `.js` and `.jsx` files only — no `.ts`, `.tsx`, or type annotations.
17
+ 3. **Validate everything at boundaries.** Server Actions and API routes must use Zod. No raw `req.body` access without parsing.
18
+ 4. **Use AppError — not Error.** Throw `AppError` / `ValidationError` / `NotFoundError` from `@techstream/quark-core/errors`. Never `throw new Error()` in application code.
19
+ 5. **Log — never console.** Use `createLogger(name)` from `@techstream/quark-core`. Zero `console.log` / `console.error` in non-test code.
20
+ 6. **DB models always have timestamps.** Every Prisma model: `createdAt DateTime @default(now())` + `updatedAt DateTime @updatedAt`.
21
+ 7. **Workspace import scope.** Use `@__QUARK_SCOPE__/*` for local packages. Never `@techstream/quark-db`, `@techstream/quark-config`, etc.
22
+ 8. **Co-located tests.** `feature.test.js` lives next to `feature.js`. Tests use `node --test` — no Jest, no Vitest.
23
+
24
+ ---
25
+
26
+ ## Import Reference
27
+
28
+ ```javascript
29
+ // ✅ Workspace packages — always @__QUARK_SCOPE__/*:
30
+ import { prisma, user, post } from "@__QUARK_SCOPE__/db";
31
+ import { loadConfig } from "@__QUARK_SCOPE__/config";
32
+ import { Button, Card, Input } from "@__QUARK_SCOPE__/ui";
33
+ import { JOB_TYPES } from "@__QUARK_SCOPE__/jobs";
34
+
35
+ // ✅ Published runtime — @techstream/quark-core:
36
+ import { AppError, ValidationError, NotFoundError } from "@techstream/quark-core/errors";
37
+ import { createLogger, getCurrentSession } from "@techstream/quark-core";
38
+ import { createQueue, addJob } from "@techstream/quark-core";
39
+ import { metrics, cache, storage } from "@techstream/quark-core";
40
+
41
+ // ❌ Never:
42
+ import { Button } from "@/components/ui/button"; // wrong path convention
43
+ import { prisma } from "@techstream/quark-db"; // wrong scope for workspace pkg
44
+ import something from "@techstream/quark-config"; // wrong scope for workspace pkg
45
+ ```
46
+
47
+ ---
48
+
49
+ ## UI & Design System
50
+
51
+ - Styling: **Tailwind CSS utility classes only**. No inline styles, no CSS modules.
52
+ - Components from `@__QUARK_SCOPE__/ui`. All accept `className` for overrides.
53
+ - Available: `Button`, `Input`, `Label`, `Textarea`, `Select`, `Checkbox`, `Badge`, `Card`/`CardHeader`/`CardTitle`/`CardContent`/`CardFooter`, `Table`/`TableHeader`/`TableBody`/`TableRow`/`TableHead`/`TableCell`, `Skeleton`, `Dialog` *(client)*, `Toast`/`useToast` *(client)*.
54
+ - Loading states → `<Skeleton>` + `<Suspense>` boundaries.
55
+ - User feedback → `useToast()` (mark component `"use client"`).
56
+ - Modals → `<Dialog>` (mark parent `"use client"`).
57
+ - Default to **Server Components**. Only add `"use client"` when necessary.
58
+
59
+ ---
60
+
61
+ ## Server Action Pattern
62
+
63
+ ```javascript
64
+ "use server";
65
+ import { z } from "zod";
66
+ import { ValidationError, AppError } from "@techstream/quark-core/errors";
67
+ import { getCurrentSession, createLogger } from "@techstream/quark-core";
68
+ import { prisma } from "@__QUARK_SCOPE__/db";
69
+
70
+ const log = createLogger("action:example");
71
+ const schema = z.object({ title: z.string().min(1).max(255) });
72
+
73
+ export async function createExample(formData) {
74
+ const session = await getCurrentSession();
75
+ if (!session) throw new AppError("Unauthorized", 401);
76
+
77
+ const parsed = schema.safeParse(Object.fromEntries(formData));
78
+ if (!parsed.success) throw new ValidationError(parsed.error.flatten());
79
+
80
+ const result = await prisma.example.create({ data: parsed.data });
81
+ log.info("created", { id: result.id });
82
+ return result;
83
+ }
84
+ ```
85
+
86
+ ---
87
+
88
+ ## API Route Pattern
89
+
90
+ ```javascript
91
+ import { NextResponse } from "next/server";
92
+ import { AppError } from "@techstream/quark-core/errors";
93
+ import { createLogger } from "@techstream/quark-core";
94
+
95
+ const log = createLogger("api:example");
96
+
97
+ export async function GET(request) {
98
+ try {
99
+ return NextResponse.json({ data });
100
+ } catch (error) {
101
+ log.error("request failed", { error });
102
+ if (error instanceof AppError)
103
+ return NextResponse.json({ error: error.message }, { status: error.statusCode });
104
+ return NextResponse.json({ error: "Internal server error" }, { status: 500 });
105
+ }
106
+ }
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Database
112
+
113
+ - Schema: `packages/db/prisma/schema.prisma`
114
+ - Query helpers: `packages/db/src/queries.js` — add functions here, don't call `prisma.*` directly in components or actions.
115
+ - Always run `pnpm db:generate` after schema changes, then `pnpm db:migrate`.
116
+
117
+ ---
118
+
119
+ ## Auth
120
+
121
+ ```javascript
122
+ import { getCurrentSession } from "@techstream/quark-core";
123
+
124
+ const session = await getCurrentSession(); // null if unauthenticated
125
+ if (!session) redirect("/login"); // Server Component
126
+ if (!session) throw new AppError("Unauthorized", 401); // Server Action / API
127
+ if (session.user.role !== "ADMIN") throw new AppError("Forbidden", 403);
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Background Jobs
133
+
134
+ ```javascript
135
+ // Dispatch (from Server Action or API route):
136
+ import { createQueue, addJob } from "@techstream/quark-core";
137
+ import { JOB_TYPES } from "@__QUARK_SCOPE__/jobs";
138
+ const queue = createQueue("default");
139
+ await addJob(queue, JOB_TYPES.MY_JOB, { userId });
140
+
141
+ // Handle (apps/worker/src/handlers/):
142
+ import { createLogger } from "@techstream/quark-core";
143
+ const log = createLogger("job:my-job");
144
+ export async function handleMyJob({ data }) { log.info("processing", data); }
145
+ ```
146
+
147
+ ---
148
+
149
+ ## Testing
150
+
151
+ ```javascript
152
+ import { test } from "node:test";
153
+ import assert from "node:assert";
154
+
155
+ test("feature", async (t) => {
156
+ await t.test("does X", async () => {
157
+ assert.strictEqual(actual, expected);
158
+ });
159
+ });
160
+ ```
161
+
162
+ ---
163
+
164
+ ## Environment
165
+
166
+ New env vars must be registered in `packages/config/src/validate-env.js` before use. Load config via `loadConfig()` from `@__QUARK_SCOPE__/config`.
167
+
168
+ ---
169
+
170
+ ## Deployment
171
+
172
+ Railway: two services (`web` + `worker`). Migrations auto-run on deploy via `releaseCommand` in `apps/web/railway.json`. Env vars managed in Railway dashboard — never committed.
@@ -5,3 +5,58 @@
5
5
  <file>.github/skills/project-context/SKILL.md</file>
6
6
  </skill>
7
7
  </skills>
8
+
9
+ # __QUARK_PROJECT_NAME__ — Project Conventions
10
+
11
+ > Scaffolded with Quark on __QUARK_SCAFFOLD_DATE__. Package scope: `@__QUARK_SCOPE__`
12
+ > Also see `CLAUDE.md` at the project root for the full context reference.
13
+
14
+ ## Non-Negotiable Rules
15
+
16
+ - **ESM only** — `import`/`export` everywhere. Never `require()`.
17
+ - **No TypeScript** — `.js` and `.jsx` only.
18
+ - **Validate at every boundary** — Zod schemas on all Server Actions and API routes.
19
+ - **Errors** — `AppError` / `ValidationError` from `@techstream/quark-core/errors`. Never `throw new Error()`.
20
+ - **Logging** — `createLogger(name)` from `@techstream/quark-core`. No `console.log` in production code.
21
+ - **DB models** — Always add `createdAt` and `updatedAt` to every Prisma model.
22
+ - **Workspace imports** — Use `@__QUARK_SCOPE__/*` for local packages (`db`, `config`, `ui`, `jobs`). Never `@techstream/quark-db` etc.
23
+ - **Tests** — Co-located `*.test.js`, run with `node --test`.
24
+
25
+ ## Import Patterns
26
+
27
+ ```javascript
28
+ // Workspace packages:
29
+ import { prisma, user } from "@__QUARK_SCOPE__/db";
30
+ import { loadConfig } from "@__QUARK_SCOPE__/config";
31
+ import { Button, Card, Input } from "@__QUARK_SCOPE__/ui";
32
+ import { JOB_TYPES } from "@__QUARK_SCOPE__/jobs";
33
+
34
+ // Published runtime:
35
+ import { AppError, ValidationError } from "@techstream/quark-core/errors";
36
+ import { createLogger, getCurrentSession, createQueue, addJob } from "@techstream/quark-core";
37
+ ```
38
+
39
+ ## UI & Design System
40
+
41
+ Components from `@__QUARK_SCOPE__/ui` — Tailwind-only, Server Component safe:
42
+ `Button`, `Input`, `Label`, `Textarea`, `Select`, `Checkbox`, `Badge`,
43
+ `Card`/`CardHeader`/`CardTitle`/`CardContent`/`CardFooter`,
44
+ `Table`/`TableHeader`/`TableBody`/`TableRow`/`TableHead`/`TableCell`,
45
+ `Skeleton`, `Dialog` *(client)*, `Toast`/`useToast` *(client)*.
46
+
47
+ All accept `className`. Never import from `@/components/ui/*`.
48
+
49
+ ## Standard Patterns
50
+
51
+ ```javascript
52
+ // Server Action:
53
+ "use server";
54
+ const parsed = schema.safeParse(Object.fromEntries(formData));
55
+ if (!parsed.success) throw new ValidationError(parsed.error.flatten());
56
+ const session = await getCurrentSession();
57
+ if (!session) throw new AppError("Unauthorized", 401);
58
+
59
+ // New env var: register in packages/config/src/validate-env.js first.
60
+ // New DB model: schema.prisma → pnpm db:generate → pnpm db:migrate.
61
+ // New job: define type in packages/jobs/src/index.js, handler in apps/worker/src/handlers/.
62
+ ```
@@ -0,0 +1,273 @@
1
+ # __QUARK_PROJECT_NAME__ — AI Context
2
+
3
+ > Scaffolded with [Quark](https://github.com/Bobnoddle/quark) on __QUARK_SCAFFOLD_DATE__.
4
+ > **Keep this file updated** as your project grows — it's what Claude Code, Cursor, and other AI tools read first.
5
+
6
+ ## Quick Start
7
+
8
+ ```bash
9
+ docker compose up -d # Start PostgreSQL, Redis, Mailpit
10
+ pnpm install # Install dependencies
11
+ pnpm db:generate # Generate Prisma client
12
+ pnpm db:migrate # Apply database migrations
13
+ pnpm db:seed # Seed development data
14
+ pnpm dev # Start web + worker
15
+ ```
16
+
17
+ ## Scaffolding Another Project (non-interactive)
18
+
19
+ ```bash
20
+ # With default features (ui + jobs):
21
+ npx @techstream/quark-create-app my-app --no-prompts
22
+
23
+ # With specific features:
24
+ npx @techstream/quark-create-app my-app --no-prompts --features ui,jobs
25
+ npx @techstream/quark-create-app my-app --no-prompts --features ui,jobs,admin
26
+ npx @techstream/quark-create-app my-app --no-prompts --features "" # minimal (db + config only)
27
+ ```
28
+
29
+ ## Project Structure
30
+
31
+ ```
32
+ __QUARK_PROJECT_NAME__/
33
+ ├── apps/
34
+ │ ├── web/ # Next.js 16 (App Router, Server Actions)
35
+ │ └── worker/ # BullMQ background worker
36
+ ├── packages/
37
+ │ ├── db/ # Prisma schema, client, query functions
38
+ │ ├── config/ # Environment validation & shared config
39
+ __QUARK_OPTIONAL_PACKAGES__├── docker-compose.yml
40
+ ├── .env # Local environment secrets (never commit)
41
+ └── .env.example # Template — copy to .env to get started
42
+ ```
43
+
44
+ ## Tech Stack
45
+
46
+ | Layer | Technology |
47
+ |---|---|
48
+ | Framework | Next.js 16 (App Router) |
49
+ | Language | JavaScript — ESM only, no TypeScript |
50
+ | Database | PostgreSQL 16 + Prisma 7 |
51
+ | Queue | BullMQ + Redis 7 |
52
+ | Auth | NextAuth v5 |
53
+ | Validation | Zod 4 |
54
+ | UI | Tailwind CSS + `@__QUARK_SCOPE__/ui` components |
55
+ | Email | Nodemailer (Mailpit local / Resend or Zeptomail prod) |
56
+ | Storage | Local filesystem or S3/R2 (pluggable via `STORAGE_PROVIDER`) |
57
+ | Logging | Structured logger via `createLogger()` from `@techstream/quark-core` |
58
+ | Metrics | Prometheus via `metrics` singleton from `@techstream/quark-core` |
59
+ | Testing | Node.js built-in `node --test` |
60
+ | Linting | Biome |
61
+ | Monorepo | Turborepo + pnpm workspaces |
62
+ | Deployment | Railway (web service + worker service) |
63
+
64
+ ## Package Imports
65
+
66
+ ```javascript
67
+ // Published (installed from npm) — use @techstream/ prefix:
68
+ import { AppError, ValidationError, NotFoundError, UnauthorizedError } from "@techstream/quark-core/errors";
69
+ import { createLogger } from "@techstream/quark-core";
70
+ import { getCurrentSession } from "@techstream/quark-core";
71
+ import { createQueue, addJob } from "@techstream/quark-core";
72
+ import { metrics } from "@techstream/quark-core";
73
+ import { cache } from "@techstream/quark-core";
74
+ import { rateLimit } from "@techstream/quark-core";
75
+ import { storage } from "@techstream/quark-core";
76
+
77
+ // Workspace packages — ALWAYS use @__QUARK_SCOPE__/* (never @techstream/quark-*):
78
+ import { prisma, user } from "@__QUARK_SCOPE__/db"; // add more as you create query helpers
79
+ import { loadConfig } from "@__QUARK_SCOPE__/config";
80
+ import { Button, Card, Input } from "@__QUARK_SCOPE__/ui"; // if ui package selected
81
+ import { JOB_TYPES } from "@__QUARK_SCOPE__/jobs"; // if jobs package selected
82
+ ```
83
+
84
+ ## UI & Design System
85
+
86
+ All UI components come from `@__QUARK_SCOPE__/ui`. They are Tailwind-only, dependency-free, and Server Component-safe.
87
+
88
+ **Available components:**
89
+ - `Button`, `Input`, `Label`, `Textarea`, `Select`, `Checkbox` — form primitives
90
+ - `Badge` — status labels
91
+ - `Card` / `CardHeader` / `CardTitle` / `CardContent` / `CardFooter` — content containers
92
+ - `Table` / `TableHeader` / `TableBody` / `TableRow` / `TableHead` / `TableCell` — data tables
93
+ - `Skeleton` — loading placeholders
94
+ - `Dialog` *(client)* — modal dialogs
95
+ - `Toast` / `useToast` *(client)* — notifications
96
+
97
+ **Rules:**
98
+ - All styling via Tailwind CSS utility classes. No inline styles, no CSS modules.
99
+ - Import from `@__QUARK_SCOPE__/ui` — never from `@/components/ui/*` or direct paths.
100
+ - Every component accepts `className` for Tailwind overrides.
101
+ - Loading states → `<Skeleton>` / `<Suspense>`.
102
+ - User feedback → `useToast()` hook (client component).
103
+ - Modals → `<Dialog>` (mark parent as `"use client"`).
104
+ - Server Components are the default — only add `"use client"` when the component uses hooks, browser APIs, or the marked client components above.
105
+
106
+ ## Server Actions (preferred for all mutations)
107
+
108
+ ```javascript
109
+ "use server";
110
+ import { z } from "zod";
111
+ import { ValidationError, AppError } from "@techstream/quark-core/errors";
112
+ import { getCurrentSession } from "@techstream/quark-core";
113
+ import { createLogger } from "@techstream/quark-core";
114
+ import { prisma } from "@__QUARK_SCOPE__/db";
115
+
116
+ const log = createLogger("action:example");
117
+
118
+ const schema = z.object({
119
+ title: z.string().min(1).max(255),
120
+ body: z.string().optional(),
121
+ });
122
+
123
+ export async function createExample(formData) {
124
+ const session = await getCurrentSession();
125
+ if (!session) throw new AppError("Unauthorized", 401);
126
+
127
+ const parsed = schema.safeParse(Object.fromEntries(formData));
128
+ if (!parsed.success) throw new ValidationError(parsed.error.flatten());
129
+
130
+ const result = await prisma.example.create({ data: parsed.data });
131
+ log.info("created", { id: result.id });
132
+ return result;
133
+ }
134
+ ```
135
+
136
+ ## API Routes
137
+
138
+ ```javascript
139
+ // apps/web/src/app/api/example/route.js
140
+ import { NextResponse } from "next/server";
141
+ import { AppError } from "@techstream/quark-core/errors";
142
+ import { createLogger } from "@techstream/quark-core";
143
+
144
+ const log = createLogger("api:example");
145
+
146
+ export async function GET(request) {
147
+ try {
148
+ // ... logic
149
+ return NextResponse.json({ data });
150
+ } catch (error) {
151
+ log.error("request failed", { error });
152
+ if (error instanceof AppError) {
153
+ return NextResponse.json({ error: error.message }, { status: error.statusCode });
154
+ }
155
+ return NextResponse.json({ error: "Internal server error" }, { status: 500 });
156
+ }
157
+ }
158
+ ```
159
+
160
+ ## Database
161
+
162
+ - Schema: `packages/db/prisma/schema.prisma`
163
+ - Query helpers: `packages/db/src/queries.js`
164
+ - Every model **must** include `createdAt DateTime @default(now())` and `updatedAt DateTime @updatedAt`.
165
+ - After any schema change: `pnpm db:generate` then `pnpm db:migrate`.
166
+
167
+ ```javascript
168
+ // Add query functions in packages/db/src/queries.js:
169
+ export const example = {
170
+ findMany: (args) => prisma.example.findMany(args),
171
+ findById: (id) => prisma.example.findUnique({ where: { id } }),
172
+ create: (data) => prisma.example.create({ data }),
173
+ update: (id, data) => prisma.example.update({ where: { id }, data }),
174
+ delete: (id) => prisma.example.delete({ where: { id } }),
175
+ };
176
+ ```
177
+
178
+ Do not call `prisma.*` directly inside pages or Server Actions — use the query helper functions.
179
+
180
+ ## Auth & Authorization
181
+
182
+ ```javascript
183
+ import { getCurrentSession } from "@techstream/quark-core";
184
+
185
+ // Server Component — check session:
186
+ const session = await getCurrentSession();
187
+ if (!session) redirect("/login");
188
+
189
+ // Server Action — guard:
190
+ const session = await getCurrentSession();
191
+ if (!session) throw new AppError("Unauthorized", 401);
192
+
193
+ // Role check (ADMIN | VIEWER defined in Prisma schema):
194
+ if (session.user.role !== "ADMIN") throw new AppError("Forbidden", 403);
195
+ ```
196
+
197
+ ## Background Jobs (if jobs package selected)
198
+
199
+ ```javascript
200
+ // 1. Dispatch from web (Server Action or API route):
201
+ import { createQueue, addJob } from "@techstream/quark-core";
202
+ import { JOB_TYPES } from "@__QUARK_SCOPE__/jobs";
203
+ const queue = createQueue("default");
204
+ await addJob(queue, JOB_TYPES.SEND_EMAIL, { userId: session.user.id, template: "welcome" });
205
+
206
+ // 2. Handle in apps/worker/src/handlers/<job-name>.js:
207
+ import { createLogger } from "@techstream/quark-core";
208
+ const log = createLogger("job:send-email");
209
+ export async function handleSendEmail({ data }) {
210
+ log.info("processing", data);
211
+ // ... job logic
212
+ }
213
+ ```
214
+
215
+ ## Environment Variables
216
+
217
+ Validated at startup in `packages/config/src/validate-env.js`. **Always add new env vars there before using them.**
218
+
219
+ Config loaded via `loadConfig()` from `@__QUARK_SCOPE__/config`.
220
+
221
+ Key variables:
222
+ | Variable | Purpose |
223
+ |---|---|
224
+ | `DATABASE_URL` / `POSTGRES_*` | PostgreSQL connection |
225
+ | `REDIS_URL` / `REDIS_HOST` + `REDIS_PORT` | Redis connection |
226
+ | `NEXTAUTH_SECRET` | Auth JWT encryption (`openssl rand -base64 32`) |
227
+ | `APP_URL` | Production domain (auto-derived from `PORT` in dev) |
228
+ | `STORAGE_PROVIDER` | `local` (default) or `s3` |
229
+ | `APP_NAME` / `APP_DESCRIPTION` | Used in metadata, emails, page titles |
230
+
231
+ ## Testing
232
+
233
+ ```javascript
234
+ import { test } from "node:test";
235
+ import assert from "node:assert";
236
+
237
+ test("feature name", async (t) => {
238
+ await t.test("does the thing", async () => {
239
+ // arrange → act → assert
240
+ assert.strictEqual(actual, expected);
241
+ });
242
+ });
243
+ ```
244
+
245
+ - Tests are co-located: `feature.test.js` lives next to `feature.js`.
246
+ - Run all: `pnpm test` | Run one package: `pnpm --filter @__QUARK_SCOPE__/web test`.
247
+ - Test utilities and model factories: `@techstream/quark-core/testing`.
248
+
249
+ ## Deployment
250
+
251
+ Railway with two services: **web** (`apps/web`) and **worker** (`apps/worker`).
252
+
253
+ - Migrations run automatically on every deploy via `releaseCommand` in `apps/web/railway.json`.
254
+ - Environment variables live in Railway dashboard — never in committed files.
255
+ - Staging: auto-deploys on push to `main`. Production: deploy from a version tag.
256
+ - Generate a production seed: `SEED_PROFILE=minimal pnpm db:seed`.
257
+
258
+ ## Key Files
259
+
260
+ | File | Purpose |
261
+ |---|---|
262
+ | `packages/db/prisma/schema.prisma` | Database schema — edit this to add models |
263
+ | `packages/db/src/queries.js` | Database query functions — add helpers here |
264
+ | `packages/config/src/validate-env.js` | Environment variable validation — register new vars here |
265
+ | `apps/web/src/app/` | Next.js pages and API routes |
266
+ | `apps/web/src/lib/auth.js` | NextAuth configuration |
267
+ | `apps/worker/src/handlers/` | Background job handler functions |
268
+ | `packages/ui/src/` | UI component source |
269
+ | `.github/skills/project-context/SKILL.md` | VS Code Copilot skill context |
270
+
271
+ ---
272
+
273
+ *Update this file whenever you make structural changes: new packages, deployment targets, major new conventions, or significant architectural decisions. The AI tools you use daily depend on it being accurate.*
@@ -1,49 +1,91 @@
1
- # My Quark Project
1
+ # __QUARK_PROJECT_NAME__
2
2
 
3
- A modern, scalable monorepo built with Quark.
3
+ Scaffolded with [Quark](https://github.com/Bobnoddle/quark) on __QUARK_SCAFFOLD_DATE__.
4
4
 
5
- ## Quick Start
5
+ ## Local Development
6
6
 
7
7
  ```bash
8
- pnpm install
9
- docker compose up -d
10
- pnpm db:migrate
11
- pnpm dev
8
+ docker compose up -d # Start PostgreSQL, Redis, Mailpit
9
+ pnpm install # Install dependencies
10
+ pnpm db:migrate # Apply migrations
11
+ pnpm dev # Start web + worker
12
12
  ```
13
13
 
14
- Open http://localhost:3000
14
+ Open [http://localhost:3000](http://localhost:3000)
15
15
 
16
- ## Services
16
+ ## Database
17
17
 
18
- - **Docker**: `docker compose up -d`
19
- - **Database**: PostgreSQL
20
- - **Cache**: Redis
21
- - **Email**: Mailpit
18
+ | Task | Command |
19
+ |------|---------|
20
+ | Run migrations | `pnpm db:migrate` |
21
+ | Push schema (no migration) | `pnpm db:push` |
22
+ | Generate Prisma client | `pnpm db:generate` |
23
+ | Seed database | `pnpm db:seed` |
24
+ | Open Prisma Studio | `pnpm db:studio` |
22
25
 
23
- ## Development
26
+ ## Other Commands
24
27
 
25
28
  ```bash
26
- # Build all packages
27
- pnpm build
29
+ pnpm build # Build all packages
30
+ pnpm test # Run all tests (requires Docker)
31
+ pnpm lint # Lint + format check (Biome)
32
+ ```
28
33
 
29
- # Run tests
30
- pnpm test
34
+ ## Launch
31
35
 
32
- # Lint
33
- pnpm lint
36
+ ### 1. Push to GitHub
37
+
38
+ ```bash
39
+ gh repo create __QUARK_PROJECT_NAME__ --private --source=. --push
34
40
  ```
35
41
 
36
- ## Database
42
+ No `gh` CLI? Go to [github.com/new](https://github.com/new), create the repo, then:
37
43
 
38
- | Task | Command |
39
- |------|--------|
40
- | Run migrations | `pnpm db:migrate` |
41
- | Push schema (no migration) | `pnpm db:push` |
42
- | Generate Prisma client | `pnpm db:generate` |
43
- | Seed database | `pnpm db:seed` |
44
- | Open Prisma Studio | `pnpm db:studio` |
44
+ ```bash
45
+ git remote add origin https://github.com/<you>/__QUARK_PROJECT_NAME__.git
46
+ git push -u origin main
47
+ ```
48
+
49
+ ### 2. Deploy on Railway
50
+
51
+ > **One-click Railway template coming soon.** For now, follow these steps:
52
+
53
+ 1. Go to [railway.app](https://railway.app) → **New Project** → **Deploy from GitHub repo** → select `__QUARK_PROJECT_NAME__`
54
+ 2. In the service → **Settings** → **Source** → set **Config File** to `apps/web/railway.json`
55
+ 3. In the project → **+ New** → **Database** → **Add PostgreSQL** (injects `DATABASE_URL` automatically)
56
+ 4. In the project → **+ New** → **Database** → **Add Redis** (injects `REDIS_URL` automatically)
57
+ 5. In your web service → **Variables** → add:
58
+ - `NEXTAUTH_SECRET` → run `openssl rand -base64 32` locally and paste the result
59
+ - `APP_URL` → your Railway public domain (e.g. `https://__QUARK_PROJECT_NAME__.railway.app`)
60
+ 6. *(If you included the worker)* **+ New** → **GitHub Repo** → same repo → **Config File** → `apps/worker/railway.json`
61
+
62
+ Railway auto-deploys on every push to `main`. Migrations run automatically before each deploy.
63
+
64
+ ## AI-Assisted Development
65
+
66
+ This project ships with pre-loaded context for Claude Code, Cursor, GitHub Copilot, and others — see `CLAUDE.md` for the full reference.
67
+
68
+ **Suggested first prompt:**
69
+
70
+ > I'm building __QUARK_PROJECT_NAME__ — [describe what your app does in one sentence].
71
+ >
72
+ > Start by reviewing `CLAUDE.md` so you understand the full stack, then:
73
+ > 1. Add the Prisma models we need to `packages/db/prisma/schema.prisma`
74
+ > 2. Create the query helpers in `packages/db/src/queries.js`
75
+ > 3. Build the first page in `apps/web/src/app/` using our UI component system
76
+
77
+ Replace that bracketed description with what you're actually building, and the AI has everything it needs.
45
78
 
46
79
  ## Structure
47
80
 
48
- - `apps/` - Applications (web, worker, etc.)
49
- - `packages/` - Shared packages (ui, jobs, config, core, db, cli)
81
+ ```
82
+ __QUARK_PROJECT_NAME__/
83
+ ├── apps/
84
+ │ ├── web/ # Next.js 16 (App Router, Server Actions)
85
+ │ └── worker/ # BullMQ background worker
86
+ ├── packages/
87
+ │ ├── db/ # Prisma schema + query helpers
88
+ │ └── config/ # Environment validation
89
+ ├── docker-compose.yml
90
+ └── CLAUDE.md # AI tool context — keep this updated
91
+ ```
@@ -43,8 +43,12 @@ const nextConfig = {
43
43
  },
44
44
  {
45
45
  key: "Content-Security-Policy",
46
+ // unsafe-eval is required by Turbopack in development only.
47
+ // It is deliberately excluded from the production directive.
46
48
  value:
47
- "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self';",
49
+ process.env.NODE_ENV !== "production"
50
+ ? "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self';"
51
+ : "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self';",
48
52
  },
49
53
  ],
50
54
  },
@@ -12,8 +12,8 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@auth/prisma-adapter": "^2.11.1",
15
- "@aws-sdk/client-s3": "^3.1002.0",
16
- "@aws-sdk/s3-request-presigner": "^3.1002.0",
15
+ "@aws-sdk/client-s3": "^3.1004.0",
16
+ "@aws-sdk/s3-request-presigner": "^3.1004.0",
17
17
  "@techstream/quark-core": "^1.0.0",
18
18
  "@techstream/quark-db": "workspace:*",
19
19
  "@techstream/quark-jobs": "workspace:*",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "devDependencies": {
30
30
  "@tailwindcss/postcss": "^4.2.1",
31
- "@types/node": "^25.3.3",
31
+ "@types/node": "^25.3.5",
32
32
  "tailwindcss": "^4.2.1",
33
33
  "@techstream/quark-config": "workspace:*"
34
34
  }