@usequark/quark-create-app 1.23.11

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 (237) hide show
  1. package/README.md +210 -0
  2. package/package.json +56 -0
  3. package/src/dependabot-config.test.js +128 -0
  4. package/src/deploy/adapters/iac.js +203 -0
  5. package/src/deploy/adapters/index.js +27 -0
  6. package/src/deploy/adapters/railway.js +508 -0
  7. package/src/deploy/adapters/railway.test.js +318 -0
  8. package/src/deploy/contract.js +59 -0
  9. package/src/deploy/deploy.integration.test.js +183 -0
  10. package/src/deploy/deploy.js +430 -0
  11. package/src/deploy/deploy.test.js +105 -0
  12. package/src/deploy/discovery.js +115 -0
  13. package/src/deploy/discovery.test.js +141 -0
  14. package/src/deploy/index.js +38 -0
  15. package/src/deploy/inspect.js +130 -0
  16. package/src/index.js +2920 -0
  17. package/src/mobile-template.test.js +232 -0
  18. package/src/prepare-hooks.test.js +535 -0
  19. package/src/scaffold-guards.test.js +140 -0
  20. package/src/scaffold-output.test.js +228 -0
  21. package/src/template-scripts.test.js +160 -0
  22. package/src/utils.js +77 -0
  23. package/src/utils.test.js +95 -0
  24. package/src/worker-dockerfile.test.js +85 -0
  25. package/templates/base-project/.cursor/rules/quark.mdc +172 -0
  26. package/templates/base-project/.dockerignore +14 -0
  27. package/templates/base-project/.env.railway.example +87 -0
  28. package/templates/base-project/.github/copilot-instructions.md +65 -0
  29. package/templates/base-project/.github/dependabot.yml +31 -0
  30. package/templates/base-project/.github/skills/project-context/SKILL.md +109 -0
  31. package/templates/base-project/.github/workflows/ci.yml +116 -0
  32. package/templates/base-project/.github/workflows/dependabot-auto-merge.yml +22 -0
  33. package/templates/base-project/.github/workflows/install-windows.yml +27 -0
  34. package/templates/base-project/.github/workflows/release.yml +67 -0
  35. package/templates/base-project/.railway/railway.ts +40 -0
  36. package/templates/base-project/CLAUDE.md +163 -0
  37. package/templates/base-project/MAIN.md +21 -0
  38. package/templates/base-project/README.md +164 -0
  39. package/templates/base-project/apps/web/.env.example +99 -0
  40. package/templates/base-project/apps/web/Dockerfile +38 -0
  41. package/templates/base-project/apps/web/biome.json +8 -0
  42. package/templates/base-project/apps/web/jsconfig.json +9 -0
  43. package/templates/base-project/apps/web/next.config.js +154 -0
  44. package/templates/base-project/apps/web/package.json +42 -0
  45. package/templates/base-project/apps/web/postcss.config.mjs +7 -0
  46. package/templates/base-project/apps/web/public/.well-known/security.txt +3 -0
  47. package/templates/base-project/apps/web/public/quark.svg +47 -0
  48. package/templates/base-project/apps/web/scripts/prepare-standalone.mjs +71 -0
  49. package/templates/base-project/apps/web/src/app/_components/AsciiPanel.js +103 -0
  50. package/templates/base-project/apps/web/src/app/_components/HealthIndicator.js +85 -0
  51. package/templates/base-project/apps/web/src/app/_components/HomeThemeToggle.js +63 -0
  52. package/templates/base-project/apps/web/src/app/_components/QuarkAnimation.js +168 -0
  53. package/templates/base-project/apps/web/src/app/_components/SyncedPanels.js +212 -0
  54. package/templates/base-project/apps/web/src/app/_components/UmamiReplayRecorder.js +198 -0
  55. package/templates/base-project/apps/web/src/app/_components/UmamiWebVitals.js +39 -0
  56. package/templates/base-project/apps/web/src/app/api/auth/[...nextauth]/route.js +7 -0
  57. package/templates/base-project/apps/web/src/app/api/auth/apple/route.js +82 -0
  58. package/templates/base-project/apps/web/src/app/api/auth/google/route.js +59 -0
  59. package/templates/base-project/apps/web/src/app/api/auth/refresh/route.js +48 -0
  60. package/templates/base-project/apps/web/src/app/api/auth/register/route.js +63 -0
  61. package/templates/base-project/apps/web/src/app/api/auth/token/route.js +44 -0
  62. package/templates/base-project/apps/web/src/app/api/csrf/route.js +40 -0
  63. package/templates/base-project/apps/web/src/app/api/device/register/route.js +57 -0
  64. package/templates/base-project/apps/web/src/app/api/error-handler.js +23 -0
  65. package/templates/base-project/apps/web/src/app/api/files/[id]/route.js +89 -0
  66. package/templates/base-project/apps/web/src/app/api/files/route.js +120 -0
  67. package/templates/base-project/apps/web/src/app/api/health/route.js +160 -0
  68. package/templates/base-project/apps/web/src/app/api/users/[id]/route.js +54 -0
  69. package/templates/base-project/apps/web/src/app/api/users/me/route.js +67 -0
  70. package/templates/base-project/apps/web/src/app/api/users/route.js +45 -0
  71. package/templates/base-project/apps/web/src/app/auth/error/page.js +83 -0
  72. package/templates/base-project/apps/web/src/app/auth/forgot-password/page.js +58 -0
  73. package/templates/base-project/apps/web/src/app/auth/provider-loading.js +25 -0
  74. package/templates/base-project/apps/web/src/app/auth/register/RegisterPageClient.js +340 -0
  75. package/templates/base-project/apps/web/src/app/auth/register/page.js +17 -0
  76. package/templates/base-project/apps/web/src/app/auth/signin/SignInPageClient.js +230 -0
  77. package/templates/base-project/apps/web/src/app/auth/signin/page.js +10 -0
  78. package/templates/base-project/apps/web/src/app/favicon.ico +0 -0
  79. package/templates/base-project/apps/web/src/app/global-error.js +58 -0
  80. package/templates/base-project/apps/web/src/app/globals.css +1166 -0
  81. package/templates/base-project/apps/web/src/app/icon.svg +47 -0
  82. package/templates/base-project/apps/web/src/app/layout.js +57 -0
  83. package/templates/base-project/apps/web/src/app/manifest.js +12 -0
  84. package/templates/base-project/apps/web/src/app/not-found.js +39 -0
  85. package/templates/base-project/apps/web/src/app/page.js +262 -0
  86. package/templates/base-project/apps/web/src/app/robots.js +23 -0
  87. package/templates/base-project/apps/web/src/app/sitemap.js +14 -0
  88. package/templates/base-project/apps/web/src/lib/analytics/umami-before-send.js +29 -0
  89. package/templates/base-project/apps/web/src/lib/analytics/umami-config.js +95 -0
  90. package/templates/base-project/apps/web/src/lib/analytics/umami-csp.js +24 -0
  91. package/templates/base-project/apps/web/src/lib/analytics/umami-marketing.js +131 -0
  92. package/templates/base-project/apps/web/src/lib/analytics/umami-replay.js +204 -0
  93. package/templates/base-project/apps/web/src/lib/analytics/umami-web-vitals.js +237 -0
  94. package/templates/base-project/apps/web/src/lib/analytics/umami.js +57 -0
  95. package/templates/base-project/apps/web/src/lib/auth-middleware.js +82 -0
  96. package/templates/base-project/apps/web/src/lib/auth-signup.js +5 -0
  97. package/templates/base-project/apps/web/src/lib/auth.js +191 -0
  98. package/templates/base-project/apps/web/src/lib/csrf-cookie.js +11 -0
  99. package/templates/base-project/apps/web/src/lib/jwt.js +84 -0
  100. package/templates/base-project/apps/web/src/lib/load-crm-config.js +35 -0
  101. package/templates/base-project/apps/web/src/lib/normalize-auth-request.js +33 -0
  102. package/templates/base-project/apps/web/src/lib/proxy-auth.js +80 -0
  103. package/templates/base-project/apps/web/src/lib/seo/indexing.js +25 -0
  104. package/templates/base-project/apps/web/src/lib/seo/site-metadata.js +74 -0
  105. package/templates/base-project/apps/web/src/lib/sitemap-entries.js +22 -0
  106. package/templates/base-project/apps/web/src/preflight.js +55 -0
  107. package/templates/base-project/apps/web/src/proxy.js +328 -0
  108. package/templates/base-project/biome.json +52 -0
  109. package/templates/base-project/docker-compose.override.yml +17 -0
  110. package/templates/base-project/docker-compose.yml +37 -0
  111. package/templates/base-project/docs/FIRST_FEATURE.md +74 -0
  112. package/templates/base-project/docs/START_HERE.md +70 -0
  113. package/templates/base-project/docs/adr/README.md +42 -0
  114. package/templates/base-project/package.json +58 -0
  115. package/templates/base-project/packages/db/package.json +38 -0
  116. package/templates/base-project/packages/db/prisma/migrations/20260202061128_initial/migration.sql +202 -0
  117. package/templates/base-project/packages/db/prisma/migrations/migration_lock.toml +3 -0
  118. package/templates/base-project/packages/db/prisma/schema.prisma +161 -0
  119. package/templates/base-project/packages/db/prisma/seed.js +152 -0
  120. package/templates/base-project/packages/db/prisma.config.js +27 -0
  121. package/templates/base-project/packages/db/src/client.js +75 -0
  122. package/templates/base-project/packages/db/src/connection.js +44 -0
  123. package/templates/base-project/packages/db/src/context.js +275 -0
  124. package/templates/base-project/packages/db/src/index.js +5 -0
  125. package/templates/base-project/packages/db/src/ping.js +66 -0
  126. package/templates/base-project/packages/db/src/queries.js +292 -0
  127. package/templates/base-project/packages/db/src/schemas.js +30 -0
  128. package/templates/base-project/packages/db/src/umami.js +299 -0
  129. package/templates/base-project/pnpm-workspace.yaml +20 -0
  130. package/templates/base-project/scripts/check-loading.mjs +49 -0
  131. package/templates/base-project/scripts/check-standards.mjs +186 -0
  132. package/templates/base-project/scripts/clean-workspace.mjs +479 -0
  133. package/templates/base-project/scripts/dev-preflight.mjs +48 -0
  134. package/templates/base-project/scripts/doctor.js +408 -0
  135. package/templates/base-project/scripts/prepare.js +490 -0
  136. package/templates/base-project/scripts/run-tests.mjs +71 -0
  137. package/templates/base-project/skills/add-dashboard/SKILL.md +28 -0
  138. package/templates/base-project/skills/add-endpoint/SKILL.md +29 -0
  139. package/templates/base-project/skills/add-model/SKILL.md +28 -0
  140. package/templates/base-project/skills/ecommerce/SKILL.md +224 -0
  141. package/templates/base-project/skills/ecommerce-cart/SKILL.md +193 -0
  142. package/templates/base-project/skills/ecommerce-catalog/SKILL.md +179 -0
  143. package/templates/base-project/skills/ecommerce-checkout/SKILL.md +195 -0
  144. package/templates/base-project/skills/i18n/SKILL.md +211 -0
  145. package/templates/base-project/skills/payment/SKILL.md +280 -0
  146. package/templates/base-project/skills/quark-skills/SKILL.md +37 -0
  147. package/templates/base-project/turbo.json +143 -0
  148. package/templates/config/package.json +20 -0
  149. package/templates/config/src/app-url.js +134 -0
  150. package/templates/config/src/environment.js +284 -0
  151. package/templates/config/src/index.js +16 -0
  152. package/templates/config/src/load-config.js +157 -0
  153. package/templates/config/src/rate-limit.js +136 -0
  154. package/templates/config/src/redis.js +43 -0
  155. package/templates/config/src/validate-env.js +532 -0
  156. package/templates/jobs/README.md +52 -0
  157. package/templates/jobs/package.json +9 -0
  158. package/templates/jobs/src/definitions.js +13 -0
  159. package/templates/jobs/src/index.js +1 -0
  160. package/templates/mobile/.env.example +16 -0
  161. package/templates/mobile/CHECKLIST.md +40 -0
  162. package/templates/mobile/README.md +130 -0
  163. package/templates/mobile/app/(app)/_layout.tsx +83 -0
  164. package/templates/mobile/app/(app)/index.tsx +147 -0
  165. package/templates/mobile/app/(app)/profile.tsx +204 -0
  166. package/templates/mobile/app/(app)/settings.tsx +80 -0
  167. package/templates/mobile/app/(auth)/_layout.tsx +10 -0
  168. package/templates/mobile/app/(auth)/sign-in.tsx +140 -0
  169. package/templates/mobile/app/(auth)/sign-up.tsx +178 -0
  170. package/templates/mobile/app/+not-found.tsx +32 -0
  171. package/templates/mobile/app/_layout.tsx +18 -0
  172. package/templates/mobile/app.json +47 -0
  173. package/templates/mobile/assets/adaptive-icon.png +0 -0
  174. package/templates/mobile/assets/icon.png +0 -0
  175. package/templates/mobile/assets/notification-icon.png +0 -0
  176. package/templates/mobile/assets/splash.png +0 -0
  177. package/templates/mobile/biome.json +27 -0
  178. package/templates/mobile/components/AuthGate.tsx +19 -0
  179. package/templates/mobile/components/ThemeProvider.tsx +23 -0
  180. package/templates/mobile/eas.json +29 -0
  181. package/templates/mobile/hooks/use-auth.ts +127 -0
  182. package/templates/mobile/lib/api-client.ts +138 -0
  183. package/templates/mobile/lib/api.ts +83 -0
  184. package/templates/mobile/lib/auth-providers-google.ts +74 -0
  185. package/templates/mobile/lib/auth-providers.ts +74 -0
  186. package/templates/mobile/lib/auth.ts +100 -0
  187. package/templates/mobile/lib/config.ts +14 -0
  188. package/templates/mobile/lib/errors.ts +46 -0
  189. package/templates/mobile/lib/notifications.ts +77 -0
  190. package/templates/mobile/lib/storage.ts +25 -0
  191. package/templates/mobile/lib/theme-tokens.ts +16 -0
  192. package/templates/mobile/package.json +37 -0
  193. package/templates/mobile/tsconfig.json +10 -0
  194. package/templates/pwa/app/_components/PWARegister.js +15 -0
  195. package/templates/pwa/app/manifest.js +32 -0
  196. package/templates/pwa/public/sw.js +72 -0
  197. package/templates/ui/README.md +44 -0
  198. package/templates/ui/package.json +21 -0
  199. package/templates/ui/src/badge.js +23 -0
  200. package/templates/ui/src/button.js +111 -0
  201. package/templates/ui/src/card.js +128 -0
  202. package/templates/ui/src/checkbox.js +20 -0
  203. package/templates/ui/src/container.js +13 -0
  204. package/templates/ui/src/dialog.js +137 -0
  205. package/templates/ui/src/error-banner.js +15 -0
  206. package/templates/ui/src/footer.js +239 -0
  207. package/templates/ui/src/form-field.integration-test.js +142 -0
  208. package/templates/ui/src/form-field.js +50 -0
  209. package/templates/ui/src/index.js +24 -0
  210. package/templates/ui/src/input.js +11 -0
  211. package/templates/ui/src/label.js +14 -0
  212. package/templates/ui/src/lightbox.integration-test.js +167 -0
  213. package/templates/ui/src/lightbox.js +243 -0
  214. package/templates/ui/src/logo.js +52 -0
  215. package/templates/ui/src/navbar.js +602 -0
  216. package/templates/ui/src/password-input.js +31 -0
  217. package/templates/ui/src/rich-text.js +514 -0
  218. package/templates/ui/src/select.js +317 -0
  219. package/templates/ui/src/skeleton.js +10 -0
  220. package/templates/ui/src/spinner.js +31 -0
  221. package/templates/ui/src/table.js +169 -0
  222. package/templates/ui/src/textarea.js +11 -0
  223. package/templates/ui/src/theme-constants.js +24 -0
  224. package/templates/ui/src/theme.js +196 -0
  225. package/templates/ui/src/toast.js +206 -0
  226. package/templates/worker/Dockerfile +64 -0
  227. package/templates/worker/README.md +56 -0
  228. package/templates/worker/package.json +36 -0
  229. package/templates/worker/src/config/pricing.js +46 -0
  230. package/templates/worker/src/handlers/email-job-validation.js +36 -0
  231. package/templates/worker/src/handlers/email.js +98 -0
  232. package/templates/worker/src/handlers/files.js +59 -0
  233. package/templates/worker/src/handlers/index.js +20 -0
  234. package/templates/worker/src/handlers/push-notification.js +234 -0
  235. package/templates/worker/src/index.js +505 -0
  236. package/templates/worker/src/lib/railway.js +438 -0
  237. package/templates/worker/src/lib/tokens.js +81 -0
@@ -0,0 +1,67 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ prerelease:
7
+ description: "Tag as pre-release (staging-only, skips production deploy)"
8
+ required: false
9
+ default: false
10
+ type: boolean
11
+
12
+ concurrency: ${{ github.workflow }}
13
+
14
+ permissions:
15
+ contents: write
16
+
17
+ jobs:
18
+ release:
19
+ name: Tag & Release
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ with:
24
+ ref: main
25
+ fetch-depth: 0
26
+
27
+ - name: Configure git
28
+ run: |
29
+ git config user.name "github-actions[bot]"
30
+ git config user.email "github-actions[bot]@users.noreply.github.com"
31
+
32
+ - name: Push to production branch
33
+ if: ${{ !inputs.prerelease }}
34
+ run: |
35
+ git fetch origin
36
+ if git ls-remote --exit-code --heads origin production > /dev/null; then
37
+ git checkout production && git merge --ff-only origin/main
38
+ else
39
+ git checkout -b production origin/main
40
+ fi
41
+ git push origin production
42
+ git checkout main
43
+
44
+ - name: Generate tag
45
+ id: tag
46
+ run: |
47
+ BASE="v$(date +%Y.%m.%d)"
48
+ SUFFIX="${{ inputs.prerelease && '-rc' || '' }}"
49
+ # Count only exact-format matches to avoid rc tags polluting production counts
50
+ EXISTING=$(git tag -l | grep -cE "^${BASE}${SUFFIX}(\.[0-9]+)?$" || true)
51
+ if [ "$EXISTING" -eq "0" ]; then
52
+ echo "tag=${BASE}${SUFFIX}" >> "$GITHUB_OUTPUT"
53
+ else
54
+ echo "tag=${BASE}${SUFFIX}.${EXISTING}" >> "$GITHUB_OUTPUT"
55
+ fi
56
+
57
+ - name: Tag release
58
+ run: |
59
+ git tag -a "${{ steps.tag.outputs.tag }}" -m "Release ${{ steps.tag.outputs.tag }}"
60
+ git push origin "${{ steps.tag.outputs.tag }}"
61
+
62
+ - name: Create GitHub Release
63
+ uses: softprops/action-gh-release@v2
64
+ with:
65
+ tag_name: ${{ steps.tag.outputs.tag }}
66
+ generate_release_notes: true
67
+ prerelease: ${{ inputs.prerelease }}
@@ -0,0 +1,40 @@
1
+ import { defineRailway, project, service } from "railway/iac";
2
+
3
+ export default defineRailway(() => {
4
+ const web = service("web", {
5
+ build: "pnpm install --frozen-lockfile && pnpm db:generate && pnpm --dir apps/web build:deploy",
6
+ start: "HOSTNAME=0.0.0.0 pnpm --dir apps/web start:deploy",
7
+ preDeploy: "pnpm db:migrate:deploy",
8
+ healthcheck: "/api/health",
9
+ healthcheckTimeout: 120,
10
+ env: {
11
+ DATABASE_URL: "${{Postgres.DATABASE_URL}}",
12
+ REDIS_URL: "${{Redis.REDIS_URL}}",
13
+ NODE_ENV: "production",
14
+ AUTH_SECRET: preserve(),
15
+ NEXTAUTH_SECRET: preserve(),
16
+ STORAGE_PROVIDER: "local",
17
+ AUTH_ALLOW_SIGNUP: "false",
18
+ HOSTNAME: "0.0.0.0",
19
+ },
20
+ });
21
+
22
+ const worker = service("worker", {
23
+ build: "pnpm install --frozen-lockfile && pnpm db:generate",
24
+ start: "pnpm --dir apps/worker start:deploy",
25
+ preDeploy: "pnpm db:migrate:deploy",
26
+ env: {
27
+ DATABASE_URL: "${{Postgres.DATABASE_URL}}",
28
+ REDIS_URL: "${{Redis.REDIS_URL}}",
29
+ NODE_ENV: "production",
30
+ AUTH_SECRET: preserve(),
31
+ NEXTAUTH_SECRET: preserve(),
32
+ STORAGE_PROVIDER: "local",
33
+ WORKER_CONCURRENCY: "5",
34
+ },
35
+ });
36
+
37
+ return project("__QUARK_PROJECT_NAME__", {
38
+ resources: [web, worker],
39
+ });
40
+ });
@@ -0,0 +1,163 @@
1
+ # __QUARK_PROJECT_NAME__ - AI Context
2
+
3
+ > Scaffolded with [Quark](https://github.com/usequark/quark) on __QUARK_SCAFFOLD_DATE__.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ docker compose up -d # PostgreSQL, Redis, Mailpit
9
+ pnpm install
10
+ pnpm db:generate # Generate Prisma client
11
+ pnpm db:migrate # Apply database migrations
12
+ pnpm db:seed # Seed development data
13
+ pnpm dev # Start web + worker
14
+ ```
15
+
16
+ ## Project Structure
17
+
18
+ ```
19
+ __QUARK_PROJECT_NAME__/
20
+ ├── apps/
21
+ │ ├── web/ # Next.js 16 (App Router, Server Actions)
22
+ __QUARK_OPTIONAL_APPS__├── packages/
23
+ │ ├── db/ # Prisma schema, client, query functions
24
+ │ ├── config/ # Environment validation & shared config
25
+ __QUARK_OPTIONAL_PACKAGES__├── docker-compose.yml
26
+ ├── .env # Local environment secrets (never commit)
27
+ └── .env.example # Template - copy to .env to get started
28
+ ```
29
+
30
+ ## Non-Negotiable Rules
31
+
32
+ - **ESM only** — `import`/`export`. Never `require()` or `module.exports`.
33
+ - **No TypeScript** — `.js` and `.jsx` files only.
34
+ - **Zod required** — all Server Actions and API routes must validate with Zod.
35
+ - **No `throw new Error()`** — use `AppError` / `ValidationError` from `@techstream/quark-core/errors`.
36
+ - **No `console.log`** — use `createLogger(name)` from `@techstream/quark-core`.
37
+ - **DB models** — every Prisma model must include `createdAt DateTime @default(now())` and `updatedAt DateTime @updatedAt`.
38
+
39
+ ## Package Imports
40
+
41
+ ```javascript
42
+ // Published (from npm) - @techstream/ prefix:
43
+ import { AppError, ValidationError } from "@techstream/quark-core/errors";
44
+ import { createLogger } from "@techstream/quark-core";
45
+ import { getCurrentSession } from "@techstream/quark-core";
46
+
47
+ // Workspace packages - ALWAYS use @__QUARK_SCOPE__/:
48
+ import { prisma } from "@__QUARK_SCOPE__/db";
49
+ import { loadConfig } from "@__QUARK_SCOPE__/config";
50
+ import { Button, Card } from "@__QUARK_SCOPE__/ui";
51
+ import { JOB_NAMES } from "@__QUARK_SCOPE__/jobs";
52
+ ```
53
+
54
+ ## UI Components
55
+
56
+ All components from `@__QUARK_SCOPE__/ui`. Tailwind-only, Server Component-safe.
57
+
58
+ - `Button`, `Input`, `Label`, `Textarea`, `Select`, `Checkbox` — form primitives
59
+ - `Badge`, `Card`/`CardHeader`/`CardTitle`/`CardContent`/`CardFooter`
60
+ - `Table`/`TableHeader`/`TableBody`/`TableRow`/`TableHead`/`TableCell`
61
+ - `Skeleton`, `ErrorBanner`, `Footer`, `Navbar`/`MobileNavbar`
62
+ - `Dialog` *(client)*, `Toast`/`useToast` *(client)*, `ThemeProvider`/`useTheme` *(client)*
63
+
64
+ Import from `@__QUARK_SCOPE__/ui` — never `@/components/ui/*`. Every component accepts `className`.
65
+
66
+ ## Server Actions
67
+
68
+ ```javascript
69
+ "use server";
70
+ import { z } from "zod";
71
+ import { ValidationError, AppError } from "@techstream/quark-core/errors";
72
+ import { getCurrentSession } from "@techstream/quark-core";
73
+ import { createLogger } from "@techstream/quark-core";
74
+ import { prisma } from "@__QUARK_SCOPE__/db";
75
+
76
+ const log = createLogger("action:example");
77
+ const schema = z.object({ title: z.string().min(1).max(255) });
78
+
79
+ export async function createExample(formData) {
80
+ const session = await getCurrentSession();
81
+ if (!session) throw new AppError("Unauthorized", 401);
82
+ const parsed = schema.safeParse(Object.fromEntries(formData));
83
+ if (!parsed.success) throw new ValidationError(parsed.error.flatten());
84
+ const result = await prisma.example.create({ data: parsed.data });
85
+ log.info("created", { id: result.id });
86
+ return result;
87
+ }
88
+ ```
89
+
90
+ ## API Routes
91
+
92
+ ```javascript
93
+ import { NextResponse } from "next/server";
94
+ import { AppError } from "@techstream/quark-core/errors";
95
+
96
+ export async function GET(request) {
97
+ try {
98
+ return NextResponse.json({ data });
99
+ } catch (error) {
100
+ if (error instanceof AppError) {
101
+ return NextResponse.json({ error: error.message }, { status: error.statusCode });
102
+ }
103
+ return NextResponse.json({ error: "Internal server error" }, { status: 500 });
104
+ }
105
+ }
106
+ ```
107
+
108
+ ## Database
109
+
110
+ - Schema: `packages/db/prisma/schema.prisma`
111
+ - Query helpers: `packages/db/src/queries.js` — do not call `prisma.*` directly in pages/actions.
112
+ - After schema changes: `pnpm db:generate` then `pnpm db:migrate`.
113
+
114
+ ## Auth
115
+
116
+ ```javascript
117
+ import { getCurrentSession } from "@techstream/quark-core";
118
+ const session = await getCurrentSession();
119
+ if (!session) redirect("/login"); // Server Component
120
+ if (!session) throw new AppError("Unauthorized", 401); // Server Action
121
+ ```
122
+
123
+ ## Background Jobs
124
+
125
+ ```javascript
126
+ // Dispatch:
127
+ import { createQueue, addJob } from "@techstream/quark-core";
128
+ import { JOB_NAMES } from "@__QUARK_SCOPE__/jobs";
129
+ await addJob(createQueue("default"), JOB_NAMES.SEND_WELCOME_EMAIL, { userId });
130
+
131
+ // Handle in apps/worker/src/handlers/<job-name>.js:
132
+ export async function handleSendEmail({ data }) { /* ... */ }
133
+ ```
134
+
135
+ ## Testing
136
+
137
+ ```bash
138
+ docker compose up -d # Required: Postgres + Redis
139
+ pnpm test # Run all tests
140
+ ```
141
+
142
+ Tests are co-located: `feature.test.js` next to `feature.js`. Uses Node.js built-in `node --test`.
143
+
144
+ ## Deployment
145
+
146
+ Railway with two services: **web** (`apps/web`) and **worker** (`apps/worker`). Migrations run automatically on deploy.
147
+
148
+ ## Key Files
149
+
150
+ | File | Purpose |
151
+ |---|---|
152
+ | `packages/db/prisma/schema.prisma` | Database schema |
153
+ | `packages/db/src/queries.js` | Query helpers |
154
+ | `packages/config/src/validate-env.js` | Env var validation |
155
+ | `apps/web/src/app/` | Next.js pages and API routes |
156
+ | `apps/web/src/lib/auth.js` | NextAuth configuration |
157
+ | `apps/worker/src/handlers/` | Job handler functions |
158
+ | `apps/web/public/sw.js` | Service worker (if PWA enabled) |
159
+ | `apps/web/src/app/manifest.json` | PWA manifest (if enabled) |
160
+
161
+ ---
162
+
163
+ *Update this file when you make structural changes: new packages, deployment targets, or major conventions.*
@@ -0,0 +1,21 @@
1
+ # __QUARK_PROJECT_NAME__
2
+
3
+ > __QUARK_PROJECT_BRIEF__
4
+
5
+ ## Read this first
6
+
7
+ 1. `CLAUDE.md` - project rules and conventions (keep this updated)
8
+ 2. `docs/` - guides and walkthroughs
9
+ 3. `openapi.yaml` - API contract
10
+ 4. `.opencode/skills/` - embedded skills (how to build bookings, CRM, CMS, AI, and more on this project)
11
+
12
+ ## Quick start
13
+
14
+ ```bash
15
+ docker compose up -d # Start PostgreSQL, Redis, Mailpit
16
+ pnpm install # Install dependencies
17
+ pnpm db:migrate # Apply migrations
18
+ pnpm dev # Start the app (and worker, if included)
19
+ ```
20
+
21
+ Open [http://localhost:3000](http://localhost:3000).
@@ -0,0 +1,164 @@
1
+ # __QUARK_PROJECT_NAME__
2
+
3
+ Scaffolded with [Quark](https://github.com/usequark/quark) on __QUARK_SCAFFOLD_DATE__.
4
+
5
+ ## Start Here
6
+
7
+ - `docs/START_HERE.md` - current onboarding path for this project
8
+ - `docs/FIRST_FEATURE.md` - add your first domain model, query helper, and page
9
+ - `.opencode/skills/` - embedded skills (add a model, endpoint, dashboard, or build a booking/CRM/CMS/AI system)
10
+ - `CLAUDE.md` - AI tool context for Claude Code, Cursor, Copilot, and others
11
+
12
+ ## Local Development
13
+
14
+ ```bash
15
+ docker compose up -d # Start PostgreSQL, Redis, Mailpit
16
+ pnpm install # Install dependencies
17
+ pnpm db:migrate # Apply migrations
18
+ pnpm db:seed # Seed the database (admin user + sample data)
19
+ pnpm dev # Start the app (and worker, if included)
20
+ ```
21
+
22
+ Open [http://localhost:3000](http://localhost:3000)
23
+
24
+ ## UI References
25
+
26
+ - `apps/web/src/app/page.js` - production-style public page built from the shared UI package
27
+ - `packages/ui/README.md` - supported component surface and props
28
+
29
+ ## Included in This Project
30
+
31
+ | Area | Status | Where to start |
32
+ |---|---|---|
33
+ | Web app | Included | `apps/web/src/app/` |
34
+ | Database | Included | `packages/db/prisma/schema.prisma` |
35
+ | Config | Included | `packages/config/src/load-config.js` |
36
+ __QUARK_FEATURE_ROWS__
37
+
38
+ Optional features can be added later with:
39
+
40
+ ```bash
41
+ npx @techstream/quark-create-app add <feature>
42
+ ```
43
+
44
+ ### PWA (Optional)
45
+
46
+ When enabled, your app includes:
47
+ - `src/app/manifest.json` — web app manifest for installability (served natively by Next.js)
48
+ - `public/sw.js` — vanilla service worker with cache-first static assets and network-first navigation
49
+ - `src/app/_components/PWARegister.js` — client component that registers the SW in production
50
+
51
+ Edit `manifest.json` to set your app name, theme color, and icons.
52
+ The service worker uses standard Web APIs with no external dependencies.
53
+
54
+ ## First Files to Edit
55
+
56
+ - `packages/db/prisma/schema.prisma` - add your domain models
57
+ - `packages/db/src/queries.js` - add reusable query helpers
58
+ - `apps/web/src/app/` - build pages, Server Actions, and route handlers
59
+ __QUARK_FIRST_EDITS__
60
+
61
+ ## Feature Guides
62
+
63
+ - `docs/FIRST_FEATURE.md` - the best first walkthrough for product work
64
+ - `packages/db/src/queries.js` - the canonical place for reusable data access
65
+ __QUARK_FEATURE_GUIDES__
66
+
67
+ ## Database
68
+
69
+ | Task | Command |
70
+ |------|---------|
71
+ | Run migrations | `pnpm db:migrate` |
72
+ | Push schema (no migration) | `pnpm db:push` |
73
+ | Generate Prisma client | `pnpm db:generate` |
74
+ | Seed database | `pnpm db:seed` |
75
+ | Open Prisma Studio | `pnpm db:studio` |
76
+
77
+ ## Other Commands
78
+
79
+ ```bash
80
+ pnpm build # Build all packages
81
+ pnpm test # Run all tests (requires Docker)
82
+ pnpm lint # Lint + format check (Biome)
83
+ pnpm clean:check # Review reclaimable build artifacts
84
+ pnpm clean # Remove local build artifacts
85
+ pnpm clean:deep # Also remove repo-local temp workspaces
86
+ QUARK_SKIP_AUTO_CLEAN=1 pnpm dev # Disable dev auto-clean for this run
87
+ npx @techstream/quark-create-app update --scaffold-check # Review scaffold-managed drift
88
+ npx @techstream/quark-create-app update --scaffold-check --fail-on-drift # CI-friendly drift check
89
+ ```
90
+
91
+ ## Launch
92
+
93
+ ### 1. Push to GitHub
94
+
95
+ ```bash
96
+ gh repo create __QUARK_PROJECT_NAME__ --private --source=. --push
97
+ ```
98
+
99
+ No `gh` CLI? Go to [github.com/new](https://github.com/new), create the repo, then:
100
+
101
+ ```bash
102
+ git remote add origin https://github.com/<you>/__QUARK_PROJECT_NAME__.git
103
+ git push -u origin main
104
+ ```
105
+
106
+ > **Nested inside an existing repository?** GitHub Actions only reads workflow
107
+ > files from the repository root, so this project's `.github/workflows/*.yml`
108
+ > will be ignored. Copy them into the parent repository's
109
+ > `.github/workflows/` and add `working-directory: <path-to-this-project>` to
110
+ > each job's steps (plus path filters so the workflows only run on changes in
111
+ > this directory).
112
+
113
+ ### 2. Deploy on Railway
114
+
115
+ > **One-click Railway template coming soon.** For now, follow these steps:
116
+
117
+ 1. Go to [railway.app](https://railway.app) → **New Project** → **Deploy from GitHub repo** → select `__QUARK_PROJECT_NAME__`
118
+ 2. In the service → **Settings** → **Source**:
119
+ - Set **Root Directory** to `/` (the repo root - Railpack needs the full monorepo context to resolve workspace dependencies)
120
+ 3. In the project → **+ New** → **Database** → **Add PostgreSQL**
121
+ 4. In the project → **+ New** → **Database** → **Add Redis**
122
+ 5. Use `.env.railway.example` as the default Railway variable set:
123
+ - Copy the non-service defaults into Railway shared variables or each service's variables as your starting point
124
+ - Wire `DATABASE_URL` and `REDIS_URL` with Railway service references or paste the managed service connection strings Railway provides
125
+ - Replace `NEXTAUTH_SECRET`, `APP_URL`, and `ADMIN_PASSWORD`
126
+ - `HOSTNAME=0.0.0.0` is included so the standalone Next server binds correctly on Railway
127
+ - Apply `DATABASE_URL`, `REDIS_URL`, `STORAGE_PROVIDER`, and `WORKER_CONCURRENCY` to the worker too if you deploy it
128
+ 6. *(If you included the worker)* **+ New** → **GitHub Repo** → same repo → **Root Directory** → `/`
129
+
130
+ Railway auto-deploys on every push to `main`. Migrations run automatically before each deploy via `preDeploy` in `.railway/railway.ts`.
131
+
132
+ If you need to seed Railway manually after the first migration, run `SEED_PROFILE=minimal pnpm db:seed` inside a Railway shell/one-off command, or from a machine using Railway's externally reachable database credentials, and provide the required admin credentials. The seed guard rejects remote seeds without an explicit profile on purpose.
133
+
134
+ > **Important:** Root Directory must be `/` (not `apps/web`) so Railpack can resolve pnpm workspace dependencies. The `.railway/railway.ts` file defines all service configuration via Infrastructure as Code.
135
+ >
136
+ > Railway starts the web service with `HOSTNAME=0.0.0.0 pnpm --dir apps/web start:deploy` (configured in `.railway/railway.ts`), which delegates to the standalone entrypoint `node .next/standalone/apps/web/server.js`. This forces the standalone Next server to bind on Railway's network interface. The `pnpm start` script uses `next start` which requires full `node_modules` - it's for local testing only.
137
+
138
+ ## AI-Assisted Development
139
+
140
+ This project ships with pre-loaded context for Claude Code, Cursor, GitHub Copilot, and others - see `CLAUDE.md` for the full reference.
141
+
142
+ **Suggested first prompt:**
143
+
144
+ > I'm building __QUARK_PROJECT_NAME__ - [describe what your app does in one sentence].
145
+ >
146
+ > Start by reviewing `CLAUDE.md` so you understand the full stack, then:
147
+ > 1. Add the Prisma models we need to `packages/db/prisma/schema.prisma`
148
+ > 2. Create the query helpers in `packages/db/src/queries.js`
149
+ > 3. Build the first page in `apps/web/src/app/` using our UI component system
150
+
151
+ Replace that bracketed description with what you're actually building, and the AI has everything it needs.
152
+
153
+ ## Structure
154
+
155
+ ```
156
+ __QUARK_PROJECT_NAME__/
157
+ ├── apps/
158
+ │ ├── web/ # Next.js 16 (App Router, Server Actions)
159
+ __QUARK_OPTIONAL_APPS__├── packages/
160
+ │ ├── db/ # Prisma schema + query helpers
161
+ │ └── config/ # Environment validation
162
+ ├── docker-compose.yml
163
+ └── CLAUDE.md # AI tool context - keep this updated
164
+ ```
@@ -0,0 +1,99 @@
1
+ # --- Database Configuration ---
2
+ # Defaults match docker-compose.yml - change for production
3
+ POSTGRES_HOST=localhost
4
+ POSTGRES_PORT=5430
5
+ POSTGRES_USER=quark_user
6
+ POSTGRES_PASSWORD=quark_password
7
+ POSTGRES_DB=quark_dev
8
+
9
+ # --- Redis Configuration ---
10
+ REDIS_HOST=localhost
11
+ REDIS_PORT=6385
12
+
13
+ # --- Mail Configuration (Mailpit in development) ---
14
+ MAIL_HOST=localhost
15
+ MAIL_SMTP_PORT=1030
16
+ MAIL_UI_PORT=8030
17
+
18
+ # --- Email Provider ---
19
+ # EMAIL_PROVIDER=smtp
20
+ # EMAIL_FROM=YourApp <noreply@yourdomain.com>
21
+
22
+ # --- NextAuth Configuration ---
23
+ # REQUIRED: must match the exact URL the web app runs on (no trailing slash)
24
+ NEXTAUTH_URL=http://localhost:3005
25
+ # Generate a secret: node --input-type=module -e "import { randomBytes } from 'node:crypto'; console.log(randomBytes(32).toString('hex'))"
26
+ NEXTAUTH_SECRET=replace_with_a_long_random_secret
27
+
28
+ # --- OAuth Providers (optional) ---
29
+ # Uncomment and fill in to enable GitHub / Google sign-in
30
+ # GITHUB_ID=your_github_oauth_app_client_id
31
+ # GITHUB_SECRET=your_github_oauth_app_client_secret
32
+ # GOOGLE_CLIENT_ID=your_google_oauth_client_id
33
+ # GOOGLE_CLIENT_SECRET=your_google_oauth_client_secret
34
+
35
+ # --- SEO / Indexing ---
36
+ # Set to "true" ONLY in your production deployment.
37
+ # Staging and preview environments should leave this unset so they are not indexed.
38
+ # ALLOW_INDEXING=true
39
+
40
+ # --- Optional: Umami Analytics ---
41
+ # Leave all three unset to disable analytics cleanly.
42
+ # Set URL + website ID to load Umami script.js in the root layout head.
43
+ # Replay stays off unless NEXT_PUBLIC_UMAMI_REPLAY_ENABLED=true.
44
+ # Umami Links and Pixels are dashboard-generated URLs, so the scaffold does not add extra env vars for them.
45
+ # Use tracked links in campaigns/emails and pixel URLs only in HTML emails or external pages.
46
+ # NEXT_PUBLIC_UMAMI_URL=https://stats.example.com
47
+ # NEXT_PUBLIC_UMAMI_WEBSITE_ID=00000000-0000-0000-0000-000000000000
48
+ # NEXT_PUBLIC_UMAMI_REPLAY_ENABLED=false
49
+
50
+ # --- Web App Configuration ---
51
+ PORT=3005
52
+ # REQUIRED for container deployments (Docker/Railway/Vercel standalone):
53
+ # HOSTNAME=0.0.0.0
54
+
55
+ # --- Booking Slots ---
56
+ # PER_SERVICE (default): each service has its own slot inventory.
57
+ # SHARED: all services share the same slot inventory.
58
+ # BOOKING_SLOT_SCOPE=PER_SERVICE
59
+
60
+ # --- Authentication ---
61
+ # Toggle this during development to enable or disable public self-service signup.
62
+ AUTH_ALLOW_SIGNUP=true
63
+
64
+ # --- Worker Configuration ---
65
+ WORKER_CONCURRENCY=5
66
+
67
+ # --- Admin Seed ---
68
+ # Used only by `pnpm db:seed` to create the initial admin user.
69
+ # ADMIN_PASSWORD is required - there is no default. Generate one:
70
+ # openssl rand -base64 24
71
+ ADMIN_EMAIL=admin@example.com
72
+ ADMIN_PASSWORD=SET_ME
73
+ # ADMIN_NAME=Admin
74
+
75
+ # --- Metrics Scraping ---
76
+ # Set to a long random secret to protect /api/metrics from public access.
77
+ # Prometheus scrapers should pass: Authorization: Bearer <token>
78
+ # Leave unset in development - the endpoint is unprotected by default.
79
+ # METRICS_TOKEN=replace_with_a_long_random_secret
80
+
81
+ # --- File Storage ---
82
+ # Default: local filesystem (./uploads). No further config needed for development.
83
+ STORAGE_PROVIDER=local
84
+ # STORAGE_LOCAL_DIR=./uploads
85
+
86
+ # --- Optional: S3-Compatible Storage (AWS S3, Cloudflare R2, MinIO) ---
87
+ # Only configure when STORAGE_PROVIDER=s3. Not required for local development.
88
+ # Install @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner in the app when enabling S3-compatible storage.
89
+ # S3_BUCKET=my-bucket
90
+ # S3_REGION=auto
91
+ # S3_ENDPOINT=https://<account_id>.r2.cloudflarestorage.com
92
+ # S3_ACCESS_KEY_ID=
93
+ # S3_SECRET_ACCESS_KEY=
94
+ # S3_PUBLIC_URL=https://pub-xxx.r2.dev
95
+
96
+ # --- Optional: CDN ---
97
+ # Provider-agnostic CDN base URL for serving assets (CloudFront, Cloudflare, Bunny, etc.).
98
+ # Falls back to /api/files when unset - works fine without a CDN.
99
+ # ASSET_CDN_URL=https://assets.example.com
@@ -0,0 +1,38 @@
1
+ FROM node:22-alpine@sha256:9385cd9f3001dfc3431e8ead12c43e9e1f87cc1b9b5c6cfd0f73865d405b27c4 AS builder
2
+
3
+ ENV NEXT_TELEMETRY_DISABLED=1
4
+
5
+ WORKDIR /app
6
+
7
+ RUN corepack enable
8
+
9
+ COPY . .
10
+
11
+ RUN pnpm install --frozen-lockfile \
12
+ && pnpm db:generate \
13
+ && pnpm --dir apps/web build:deploy
14
+
15
+ FROM node:22-alpine@sha256:9385cd9f3001dfc3431e8ead12c43e9e1f87cc1b9b5c6cfd0f73865d405b27c4 AS runtime
16
+
17
+ RUN apk upgrade --no-cache
18
+
19
+ ENV NODE_ENV=production
20
+ ENV NEXT_TELEMETRY_DISABLED=1
21
+ ENV HOSTNAME=0.0.0.0
22
+ ENV PORT=3000
23
+
24
+ WORKDIR /app
25
+
26
+ RUN rm -rf \
27
+ /usr/local/lib/node_modules/npm \
28
+ /usr/local/bin/npm \
29
+ /usr/local/bin/npx \
30
+ /opt/yarn-v1.22.22 \
31
+ /usr/local/bin/yarn \
32
+ /usr/local/bin/yarnpkg
33
+
34
+ COPY --from=builder /app/apps/web/.next/standalone/apps/web/ ./
35
+
36
+ EXPOSE 3000
37
+
38
+ CMD ["node", "server.js"]
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
3
+ "root": false,
4
+ "extends": ["../../biome.json"],
5
+ "files": {
6
+ "includes": ["*.{js,ts,mjs,cjs,json}", "src/**", "scripts/**"]
7
+ }
8
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "compilerOptions": {
3
+ "paths": {
4
+ "@/*": ["./src/*"]
5
+ },
6
+ "jsx": "preserve"
7
+ },
8
+ "include": ["next.config.js"]
9
+ }