@yosr-ai/template-next-fullstack 0.0.0-stage → 0.1.0-alpha.1

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 (125) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +15 -2
  3. package/features/api-trpc/files/src/app/api/trpc/[trpc]/route.ts +15 -0
  4. package/features/api-trpc/files/src/app/trpc-provider.tsx +27 -0
  5. package/features/api-trpc/files/src/features/notes/components/note-form.tsx +48 -0
  6. package/features/api-trpc/files/src/server/api/context.ts +8 -0
  7. package/features/api-trpc/files/src/server/api/notes.test.ts +30 -0
  8. package/features/api-trpc/files/src/server/api/notes.ts +42 -0
  9. package/features/api-trpc/files/src/server/api/root.ts +8 -0
  10. package/features/api-trpc/files/src/server/api/trpc.ts +8 -0
  11. package/features/api-trpc/snippets/providers-imports.tsx +2 -0
  12. package/features/api-trpc/snippets/providers.tsx +1 -0
  13. package/features/auth-authjs/files/e2e/sign-in.spec.ts +10 -0
  14. package/features/auth-authjs/files/src/app/api/auth/[...nextauth]/route.ts +13 -0
  15. package/features/auth-authjs/files/src/app/auth-session-provider.tsx +8 -0
  16. package/features/auth-authjs/files/src/app/sign-in/page.tsx +19 -0
  17. package/features/auth-authjs/files/src/server/auth/config.ts +28 -0
  18. package/features/auth-authjs/files/src/server/auth/index.ts +15 -0
  19. package/features/auth-authjs/snippets/providers-imports.tsx +2 -0
  20. package/features/auth-authjs/snippets/providers.tsx +1 -0
  21. package/features/auth-authjs-drizzle/files/src/server/db/auth-tables.ts +86 -0
  22. package/features/auth-authjs-drizzle/snippets/adapter-db-imports.ts +8 -0
  23. package/features/auth-authjs-drizzle/snippets/adapter-imports.ts +1 -0
  24. package/features/auth-authjs-drizzle/snippets/adapter.ts +7 -0
  25. package/features/auth-authjs-drizzle/snippets/auth-tables.ts +7 -0
  26. package/features/auth-authjs-prisma/snippets/adapter-db-imports.ts +1 -0
  27. package/features/auth-authjs-prisma/snippets/adapter-imports.ts +1 -0
  28. package/features/auth-authjs-prisma/snippets/adapter.ts +1 -0
  29. package/features/auth-authjs-prisma/snippets/auth-models.prisma +58 -0
  30. package/features/auth-clerk/files/e2e/sign-in.spec.ts +15 -0
  31. package/features/auth-clerk/files/src/app/clerk-app-provider.tsx +13 -0
  32. package/features/auth-clerk/files/src/app/sign-in/[[...sign-in]]/page.tsx +18 -0
  33. package/features/auth-clerk/files/src/app/sign-up/[[...sign-up]]/page.tsx +18 -0
  34. package/features/auth-clerk/files/src/proxy.ts +24 -0
  35. package/features/auth-clerk/files/src/server/auth/index.ts +11 -0
  36. package/features/auth-clerk/snippets/providers-imports.tsx +2 -0
  37. package/features/auth-clerk/snippets/providers.tsx +1 -0
  38. package/features/deploy-docker/files/.dockerignore +6 -0
  39. package/features/deploy-docker/files/Dockerfile +26 -0
  40. package/features/deploy-docker/files/compose.yaml +6 -0
  41. package/features/deploy-docker-npm/files/Dockerfile +26 -0
  42. package/features/deploy-docker-postgres/snippets/postgres-service.yaml +4 -0
  43. package/features/deploy-vercel/snippets/readme-deploy.md +3 -0
  44. package/features/orm-drizzle/files/drizzle.config.ts +8 -0
  45. package/features/orm-drizzle/files/src/features/notes/notes-store.ts +21 -0
  46. package/features/orm-drizzle/files/src/server/db/index.ts +20 -0
  47. package/features/orm-drizzle/files/src/server/db/schema.ts +11 -0
  48. package/features/orm-drizzle/snippets/ci-e2e-setup.yaml +1 -0
  49. package/features/orm-drizzle/snippets/ci-job.yaml +11 -0
  50. package/features/orm-drizzle/snippets/readme-db.md +4 -0
  51. package/features/orm-prisma/files/prisma/schema.prisma +18 -0
  52. package/features/orm-prisma/files/prisma.config.ts +13 -0
  53. package/features/orm-prisma/files/src/features/notes/notes-store.ts +13 -0
  54. package/features/orm-prisma/files/src/server/db/index.ts +20 -0
  55. package/features/orm-prisma/snippets/ci-e2e-setup.yaml +1 -0
  56. package/features/orm-prisma/snippets/ci-job.yaml +11 -0
  57. package/features/orm-prisma/snippets/readme-db.md +4 -0
  58. package/features/payments-stripe/files/src/app/api/checkout/route.ts +27 -0
  59. package/features/payments-stripe/files/src/app/api/webhooks/stripe/route.ts +18 -0
  60. package/features/payments-stripe/files/src/server/billing/billing.test.ts +113 -0
  61. package/features/payments-stripe/files/src/server/billing/index.ts +99 -0
  62. package/features/payments-stripe/files/src/server/billing/stripe.ts +10 -0
  63. package/features/payments-stripe-drizzle/files/src/server/billing/store.ts +39 -0
  64. package/features/payments-stripe-drizzle/snippets/billing-tables.ts +13 -0
  65. package/features/payments-stripe-prisma/files/src/server/billing/store.ts +30 -0
  66. package/features/payments-stripe-prisma/snippets/billing-models.prisma +17 -0
  67. package/files/.env.example +1 -0
  68. package/files/.github/workflows/ci.yml +25 -0
  69. package/files/.prettierignore +7 -0
  70. package/files/.prettierrc.json +1 -0
  71. package/files/README.md +18 -0
  72. package/files/components.json +20 -0
  73. package/files/e2e/smoke.spec.ts +16 -0
  74. package/files/eslint.config.mjs +21 -0
  75. package/files/gitignore +25 -0
  76. package/files/next.config.ts +7 -0
  77. package/files/package.json +42 -0
  78. package/files/playwright.config.ts +20 -0
  79. package/files/pnpm-workspace.yaml +16 -0
  80. package/files/postcss.config.mjs +7 -0
  81. package/files/src/app/globals.css +68 -0
  82. package/files/src/app/layout.tsx +22 -0
  83. package/files/src/app/page.tsx +21 -0
  84. package/files/src/app/providers.tsx +15 -0
  85. package/files/src/components/ui/button.tsx +59 -0
  86. package/files/src/components/ui/input.tsx +21 -0
  87. package/files/src/features/notes/actions.ts +22 -0
  88. package/files/src/features/notes/components/note-form.tsx +32 -0
  89. package/files/src/features/notes/components/note-list.tsx +23 -0
  90. package/files/src/features/notes/create-note.test.ts +27 -0
  91. package/files/src/features/notes/create-note.ts +23 -0
  92. package/files/src/features/notes/note-schema.ts +13 -0
  93. package/files/src/features/notes/notes-store.ts +17 -0
  94. package/files/src/shared/utils.test.ts +13 -0
  95. package/files/src/shared/utils.ts +6 -0
  96. package/files/tsconfig.json +35 -0
  97. package/files/vitest.config.mts +9 -0
  98. package/package.json +26 -4
  99. package/rules/api-server-actions.md +1 -0
  100. package/rules/api-trpc.md +1 -0
  101. package/rules/architecture.md +11 -0
  102. package/rules/auth-authjs.md +1 -0
  103. package/rules/auth-clerk.md +1 -0
  104. package/rules/commands.md +5 -0
  105. package/rules/data-drizzle.md +1 -0
  106. package/rules/data-none.md +1 -0
  107. package/rules/data-prisma.md +1 -0
  108. package/rules/data-server-first.md +1 -0
  109. package/rules/errors-result.md +1 -0
  110. package/rules/errors-throw.md +1 -0
  111. package/rules/forms-zod.md +1 -0
  112. package/rules/layout-feature.md +5 -0
  113. package/rules/layout-flat.md +5 -0
  114. package/rules/layout-layer.md +6 -0
  115. package/rules/payments-stripe.md +1 -0
  116. package/rules/project-overview.md +5 -0
  117. package/rules/tests-colocated.md +1 -0
  118. package/rules/tests-folder.md +1 -0
  119. package/rules/tests-separate.md +1 -0
  120. package/rules/ui-shadcn.md +1 -0
  121. package/seed.template.json +1721 -0
  122. package/style/errors-result/files/src/features/notes/actions.ts +18 -0
  123. package/style/errors-result/files/src/features/notes/create-note.test.ts +30 -0
  124. package/style/errors-result/files/src/features/notes/create-note.ts +16 -0
  125. package/style/errors-result/files/src/shared/result.ts +12 -0
@@ -0,0 +1,1721 @@
1
+ {
2
+ "$schema": "../../../docs/engineering/schemas/seed.template.schema.json",
3
+ "schemaVersion": "0.1",
4
+ "name": "next-fullstack",
5
+ "version": "0.1.0-alpha.1",
6
+ "title": "Next.js full-stack (TypeScript)",
7
+ "description": "Prototype of Yosr Seed's first base template (Next.js + TypeScript, Tailwind + shadcn/ui, Vitest + Playwright smoke test). Implemented: base files with the ADR 0006 defaults, the orm-drizzle and orm-prisma overlays (database=postgres), database=none (base without either overlay), deploy=docker (standalone image, compose Postgres only when database=postgres), Auth.js, Clerk, tRPC, Stripe, and a recorded AI delta for domain-features. x-prototype.stubbedFeatures is empty. Dependency versions pinned from the npm registry on 2026-10-19 (Prisma adapter on 2026-10-28, Clerk on 2026-10-28, tRPC on 2026-10-29, Stripe on 2026-10-29).",
8
+ "license": "MIT",
9
+ "authors": [
10
+ "Yosr Seed"
11
+ ],
12
+ "keywords": [
13
+ "nextjs",
14
+ "typescript",
15
+ "tailwind",
16
+ "shadcn",
17
+ "postgres"
18
+ ],
19
+ "engines": {
20
+ "yosr": ">=0.1.0 <0.2.0",
21
+ "node": "^22.13.0 || ^24.0.0 || >=26.0.0"
22
+ },
23
+ "stack": {
24
+ "framework": "nextjs",
25
+ "language": "typescript",
26
+ "includes": [
27
+ "tailwindcss",
28
+ "shadcn-ui",
29
+ "vitest",
30
+ "playwright"
31
+ ]
32
+ },
33
+ "packageManagers": {
34
+ "default": "pnpm",
35
+ "supported": [
36
+ "pnpm",
37
+ "npm"
38
+ ]
39
+ },
40
+ "variables": {
41
+ "authLabel": {
42
+ "fromOption": "auth",
43
+ "map": {
44
+ "none": "none",
45
+ "authjs": "Auth.js",
46
+ "clerk": "Clerk"
47
+ }
48
+ },
49
+ "ormLabel": {
50
+ "fromOption": "orm",
51
+ "map": {
52
+ "prisma": "Prisma",
53
+ "drizzle": "Drizzle"
54
+ }
55
+ },
56
+ "storageLabel": {
57
+ "fromOption": "database",
58
+ "map": {
59
+ "none": "memory only (no database)",
60
+ "postgres": "Postgres"
61
+ }
62
+ },
63
+ "homeStorage": {
64
+ "fromOption": "database",
65
+ "map": {
66
+ "none": "kept in memory",
67
+ "postgres": "stored in Postgres"
68
+ }
69
+ }
70
+ },
71
+ "options": [
72
+ {
73
+ "id": "projectName",
74
+ "type": "string",
75
+ "label": {
76
+ "cli": "Project name",
77
+ "wizard": "What should we call your project?"
78
+ },
79
+ "help": {
80
+ "cli": "Lowercase, digits and dashes.",
81
+ "wizard": "A short name with no spaces. You can change the display name later."
82
+ },
83
+ "pattern": "^[a-z][a-z0-9-]*$",
84
+ "maxLength": 64
85
+ },
86
+ {
87
+ "id": "database",
88
+ "type": "enum",
89
+ "label": {
90
+ "cli": "Database",
91
+ "wizard": "Does your app need to save data?"
92
+ },
93
+ "help": {
94
+ "cli": "Postgres, or none for a frontend/static-first app.",
95
+ "wizard": "Pick 'Yes' if the app stores things like users, posts or orders."
96
+ },
97
+ "default": "postgres",
98
+ "choices": [
99
+ {
100
+ "value": "none",
101
+ "label": {
102
+ "cli": "None",
103
+ "wizard": "No, it doesn't need to save data"
104
+ }
105
+ },
106
+ {
107
+ "value": "postgres",
108
+ "label": {
109
+ "cli": "Postgres",
110
+ "wizard": "Yes, save data in a Postgres database"
111
+ }
112
+ }
113
+ ]
114
+ },
115
+ {
116
+ "id": "orm",
117
+ "type": "enum",
118
+ "label": {
119
+ "cli": "Postgres ORM",
120
+ "wizard": "How should the app talk to its database?"
121
+ },
122
+ "help": {
123
+ "cli": "Only asked when database is Postgres.",
124
+ "wizard": "Both work well; Drizzle is the default."
125
+ },
126
+ "default": "drizzle",
127
+ "choices": [
128
+ {
129
+ "value": "drizzle",
130
+ "label": {
131
+ "cli": "Drizzle",
132
+ "wizard": "Drizzle (schema in TypeScript, lightweight)"
133
+ }
134
+ },
135
+ {
136
+ "value": "prisma",
137
+ "label": {
138
+ "cli": "Prisma",
139
+ "wizard": "Prisma (schema file, very popular)"
140
+ }
141
+ }
142
+ ],
143
+ "when": {
144
+ "option": "database",
145
+ "equals": "postgres"
146
+ }
147
+ },
148
+ {
149
+ "id": "auth",
150
+ "type": "enum",
151
+ "label": {
152
+ "cli": "Authentication",
153
+ "wizard": "Do people need to sign in?"
154
+ },
155
+ "default": "none",
156
+ "choices": [
157
+ {
158
+ "value": "none",
159
+ "label": {
160
+ "cli": "None",
161
+ "wizard": "No, anyone can use it"
162
+ }
163
+ },
164
+ {
165
+ "value": "authjs",
166
+ "label": {
167
+ "cli": "Auth.js",
168
+ "wizard": "Yes, and I want to host sign-in myself"
169
+ },
170
+ "help": {
171
+ "cli": "Self-hosted, database sessions via the chosen ORM. Requires database=postgres.",
172
+ "wizard": "Free and open source. Sign-in data lives in your own database, so it needs one."
173
+ }
174
+ },
175
+ {
176
+ "value": "clerk",
177
+ "label": {
178
+ "cli": "Clerk",
179
+ "wizard": "Yes, using a hosted sign-in service"
180
+ },
181
+ "help": {
182
+ "cli": "Hosted auth; works with or without a database. Requires a Clerk account and keys.",
183
+ "wizard": "Clerk handles sign-in screens and accounts for you. Needs a Clerk account; works even without a database."
184
+ }
185
+ }
186
+ ]
187
+ },
188
+ {
189
+ "id": "api",
190
+ "type": "enum",
191
+ "label": {
192
+ "cli": "API style",
193
+ "wizard": "How should the front end call the back end?"
194
+ },
195
+ "default": "server-actions",
196
+ "choices": [
197
+ {
198
+ "value": "server-actions",
199
+ "label": {
200
+ "cli": "Server actions",
201
+ "wizard": "Built-in Next.js server actions (simplest)"
202
+ }
203
+ },
204
+ {
205
+ "value": "trpc",
206
+ "label": {
207
+ "cli": "tRPC",
208
+ "wizard": "tRPC (typed API layer, good for larger apps)"
209
+ }
210
+ }
211
+ ]
212
+ },
213
+ {
214
+ "id": "payments",
215
+ "type": "boolean",
216
+ "label": {
217
+ "cli": "Add Stripe payments?",
218
+ "wizard": "Will you charge money in the app?"
219
+ },
220
+ "help": {
221
+ "cli": "Requires auth and database=postgres.",
222
+ "wizard": "Adds Stripe checkout. Needs sign-in and a database to remember who paid."
223
+ },
224
+ "default": false,
225
+ "when": {
226
+ "all": [
227
+ {
228
+ "not": {
229
+ "option": "auth",
230
+ "equals": "none"
231
+ }
232
+ },
233
+ {
234
+ "option": "database",
235
+ "equals": "postgres"
236
+ }
237
+ ]
238
+ }
239
+ },
240
+ {
241
+ "id": "deploy",
242
+ "type": "enum",
243
+ "label": {
244
+ "cli": "Deploy target",
245
+ "wizard": "Where will the app run?"
246
+ },
247
+ "default": "vercel",
248
+ "choices": [
249
+ {
250
+ "value": "vercel",
251
+ "label": {
252
+ "cli": "Vercel",
253
+ "wizard": "Vercel (easiest for Next.js)"
254
+ }
255
+ },
256
+ {
257
+ "value": "docker",
258
+ "label": {
259
+ "cli": "Docker",
260
+ "wizard": "Docker container (any server or cloud)"
261
+ }
262
+ }
263
+ ]
264
+ }
265
+ ],
266
+ "constraints": [
267
+ {
268
+ "id": "orm-requires-database",
269
+ "if": {
270
+ "not": {
271
+ "option": "database",
272
+ "equals": "postgres"
273
+ }
274
+ },
275
+ "then": {
276
+ "not": {
277
+ "option": "orm",
278
+ "in": [
279
+ "drizzle",
280
+ "prisma"
281
+ ]
282
+ }
283
+ },
284
+ "message": {
285
+ "cli": "--orm only applies with --database postgres.",
286
+ "wizard": "Choosing how to talk to the database only matters if you have one."
287
+ }
288
+ },
289
+ {
290
+ "id": "authjs-requires-database",
291
+ "if": {
292
+ "option": "auth",
293
+ "equals": "authjs"
294
+ },
295
+ "then": {
296
+ "option": "database",
297
+ "equals": "postgres"
298
+ },
299
+ "message": {
300
+ "cli": "--auth authjs requires --database postgres (database sessions). Use --auth clerk for auth without a database.",
301
+ "wizard": "Self-hosted sign-in needs a database. Turn on the database, or pick the hosted sign-in option."
302
+ }
303
+ },
304
+ {
305
+ "id": "payments-require-auth",
306
+ "if": {
307
+ "option": "payments",
308
+ "equals": true
309
+ },
310
+ "then": {
311
+ "not": {
312
+ "option": "auth",
313
+ "equals": "none"
314
+ }
315
+ },
316
+ "message": {
317
+ "cli": "--payments requires --auth authjs or clerk.",
318
+ "wizard": "Payments need sign-in, so we can tell who paid."
319
+ }
320
+ },
321
+ {
322
+ "id": "payments-require-database",
323
+ "if": {
324
+ "option": "payments",
325
+ "equals": true
326
+ },
327
+ "then": {
328
+ "option": "database",
329
+ "equals": "postgres"
330
+ },
331
+ "message": {
332
+ "cli": "--payments requires --database postgres.",
333
+ "wizard": "Payments need a database to remember customers and subscriptions."
334
+ }
335
+ }
336
+ ],
337
+ "aliases": {
338
+ "@/*": "src/*",
339
+ "@domain/*": "src/features/*",
340
+ "@featureUi/*": "src/features/notes/components/*",
341
+ "@server/*": "src/server/*",
342
+ "@ui/*": "src/components/ui/*",
343
+ "@shared/*": "src/shared/*"
344
+ },
345
+ "features": [
346
+ {
347
+ "id": "package-manager-npm",
348
+ "description": "Drop pnpm-only files when the package manager is npm.",
349
+ "when": {
350
+ "option": "packageManager",
351
+ "equals": "npm"
352
+ },
353
+ "patches": [
354
+ {
355
+ "op": "delete",
356
+ "target": "pnpm-workspace.yaml"
357
+ }
358
+ ]
359
+ },
360
+ {
361
+ "id": "orm-drizzle",
362
+ "description": "Drizzle ORM, drizzle-kit config, TS schema and db helper.",
363
+ "when": {
364
+ "all": [
365
+ {
366
+ "option": "database",
367
+ "equals": "postgres"
368
+ },
369
+ {
370
+ "option": "orm",
371
+ "equals": "drizzle"
372
+ }
373
+ ]
374
+ },
375
+ "dependencies": {
376
+ "drizzle-orm": "0.45.4",
377
+ "postgres": "3.4.9"
378
+ },
379
+ "devDependencies": {
380
+ "drizzle-kit": "0.31.11"
381
+ },
382
+ "scripts": {
383
+ "db:generate": "drizzle-kit generate",
384
+ "db:migrate": "drizzle-kit migrate",
385
+ "db:push": "drizzle-kit push --force"
386
+ },
387
+ "patches": [
388
+ {
389
+ "op": "env",
390
+ "target": ".env.example",
391
+ "vars": [
392
+ {
393
+ "name": "DATABASE_URL",
394
+ "example": "postgresql://postgres:postgres@localhost:5432/app",
395
+ "description": "Postgres connection string"
396
+ }
397
+ ]
398
+ },
399
+ {
400
+ "op": "insert",
401
+ "target": ".github/workflows/ci.yml",
402
+ "anchor": "ci-job",
403
+ "source": "features/orm-drizzle/snippets/ci-job.yaml"
404
+ },
405
+ {
406
+ "op": "insert",
407
+ "target": ".github/workflows/ci.yml",
408
+ "anchor": "ci-e2e-setup",
409
+ "source": "features/orm-drizzle/snippets/ci-e2e-setup.yaml"
410
+ },
411
+ {
412
+ "op": "insert",
413
+ "target": "README.md",
414
+ "anchor": "readme-sections",
415
+ "source": "features/orm-drizzle/snippets/readme-db.md"
416
+ }
417
+ ],
418
+ "replaces": [
419
+ "src/features/notes/notes-store.ts"
420
+ ]
421
+ },
422
+ {
423
+ "id": "orm-prisma",
424
+ "description": "Prisma client, schema and db helper.",
425
+ "when": {
426
+ "all": [
427
+ {
428
+ "option": "database",
429
+ "equals": "postgres"
430
+ },
431
+ {
432
+ "option": "orm",
433
+ "equals": "prisma"
434
+ }
435
+ ]
436
+ },
437
+ "dependencies": {
438
+ "@prisma/adapter-pg": "7.10.1",
439
+ "@prisma/client": "7.10.1"
440
+ },
441
+ "devDependencies": {
442
+ "prisma": "7.10.1"
443
+ },
444
+ "scripts": {
445
+ "postinstall": "prisma generate",
446
+ "db:generate": "prisma generate",
447
+ "db:migrate": "prisma migrate dev",
448
+ "db:push": "prisma db push --accept-data-loss"
449
+ },
450
+ "patches": [
451
+ {
452
+ "op": "env",
453
+ "target": ".env.example",
454
+ "vars": [
455
+ {
456
+ "name": "DATABASE_URL",
457
+ "example": "postgresql://postgres:postgres@localhost:5432/app",
458
+ "description": "Postgres connection string"
459
+ }
460
+ ]
461
+ },
462
+ {
463
+ "op": "insert",
464
+ "target": ".github/workflows/ci.yml",
465
+ "anchor": "ci-job",
466
+ "source": "features/orm-prisma/snippets/ci-job.yaml"
467
+ },
468
+ {
469
+ "op": "insert",
470
+ "target": ".github/workflows/ci.yml",
471
+ "anchor": "ci-e2e-setup",
472
+ "source": "features/orm-prisma/snippets/ci-e2e-setup.yaml"
473
+ },
474
+ {
475
+ "op": "insert",
476
+ "target": "README.md",
477
+ "anchor": "readme-sections",
478
+ "source": "features/orm-prisma/snippets/readme-db.md"
479
+ }
480
+ ],
481
+ "replaces": [
482
+ "src/features/notes/notes-store.ts"
483
+ ]
484
+ },
485
+ {
486
+ "id": "auth-authjs",
487
+ "description": "Auth.js with database sessions (adapter added by the ORM combination feature): route handler, session helper, sign-in page, provider wrapper. Only valid with database=postgres.",
488
+ "when": {
489
+ "option": "auth",
490
+ "equals": "authjs"
491
+ },
492
+ "after": [
493
+ "orm-prisma",
494
+ "orm-drizzle"
495
+ ],
496
+ "dependencies": {
497
+ "next-auth": "4.24.15"
498
+ },
499
+ "patches": [
500
+ {
501
+ "op": "env",
502
+ "target": ".env.example",
503
+ "vars": [
504
+ {
505
+ "name": "AUTH_SECRET",
506
+ "description": "Random secret used to sign sessions"
507
+ },
508
+ {
509
+ "name": "GITHUB_ID",
510
+ "description": "GitHub OAuth client id"
511
+ },
512
+ {
513
+ "name": "GITHUB_SECRET",
514
+ "description": "GitHub OAuth client secret"
515
+ },
516
+ {
517
+ "name": "GOOGLE_CLIENT_ID",
518
+ "description": "Google OAuth client id"
519
+ },
520
+ {
521
+ "name": "GOOGLE_CLIENT_SECRET",
522
+ "description": "Google OAuth client secret"
523
+ }
524
+ ]
525
+ },
526
+ {
527
+ "op": "insert",
528
+ "target": "src/app/providers.tsx",
529
+ "anchor": "providers-imports",
530
+ "source": "features/auth-authjs/snippets/providers-imports.tsx"
531
+ },
532
+ {
533
+ "op": "insert",
534
+ "target": "src/app/providers.tsx",
535
+ "anchor": "providers",
536
+ "source": "features/auth-authjs/snippets/providers.tsx"
537
+ }
538
+ ]
539
+ },
540
+ {
541
+ "id": "auth-authjs-prisma",
542
+ "description": "Combination overlay: Auth.js adapter and auth models for Prisma.",
543
+ "when": {
544
+ "all": [
545
+ {
546
+ "option": "auth",
547
+ "equals": "authjs"
548
+ },
549
+ {
550
+ "all": [
551
+ {
552
+ "option": "database",
553
+ "equals": "postgres"
554
+ },
555
+ {
556
+ "option": "orm",
557
+ "equals": "prisma"
558
+ }
559
+ ]
560
+ }
561
+ ]
562
+ },
563
+ "after": [
564
+ "auth-authjs"
565
+ ],
566
+ "dependencies": {
567
+ "@auth/prisma-adapter": "2.11.3"
568
+ },
569
+ "patches": [
570
+ {
571
+ "op": "insert",
572
+ "target": "prisma/schema.prisma",
573
+ "anchor": "models",
574
+ "source": "features/auth-authjs-prisma/snippets/auth-models.prisma"
575
+ },
576
+ {
577
+ "op": "insert",
578
+ "target": "src/server/auth/config.ts",
579
+ "anchor": "adapter-imports",
580
+ "source": "features/auth-authjs-prisma/snippets/adapter-imports.ts"
581
+ },
582
+ {
583
+ "op": "insert",
584
+ "target": "src/server/auth/config.ts",
585
+ "anchor": "adapter-db-imports",
586
+ "source": "features/auth-authjs-prisma/snippets/adapter-db-imports.ts"
587
+ },
588
+ {
589
+ "op": "insert",
590
+ "target": "src/server/auth/config.ts",
591
+ "anchor": "adapter",
592
+ "source": "features/auth-authjs-prisma/snippets/adapter.ts"
593
+ }
594
+ ]
595
+ },
596
+ {
597
+ "id": "auth-authjs-drizzle",
598
+ "description": "Combination overlay: Auth.js adapter and auth tables for Drizzle.",
599
+ "when": {
600
+ "all": [
601
+ {
602
+ "option": "auth",
603
+ "equals": "authjs"
604
+ },
605
+ {
606
+ "all": [
607
+ {
608
+ "option": "database",
609
+ "equals": "postgres"
610
+ },
611
+ {
612
+ "option": "orm",
613
+ "equals": "drizzle"
614
+ }
615
+ ]
616
+ }
617
+ ]
618
+ },
619
+ "after": [
620
+ "auth-authjs"
621
+ ],
622
+ "dependencies": {
623
+ "@auth/drizzle-adapter": "1.11.3"
624
+ },
625
+ "patches": [
626
+ {
627
+ "op": "insert",
628
+ "target": "src/server/db/schema.ts",
629
+ "anchor": "tables",
630
+ "source": "features/auth-authjs-drizzle/snippets/auth-tables.ts"
631
+ },
632
+ {
633
+ "op": "insert",
634
+ "target": "src/server/auth/config.ts",
635
+ "anchor": "adapter-imports",
636
+ "source": "features/auth-authjs-drizzle/snippets/adapter-imports.ts"
637
+ },
638
+ {
639
+ "op": "insert",
640
+ "target": "src/server/auth/config.ts",
641
+ "anchor": "adapter-db-imports",
642
+ "source": "features/auth-authjs-drizzle/snippets/adapter-db-imports.ts"
643
+ },
644
+ {
645
+ "op": "insert",
646
+ "target": "src/server/auth/config.ts",
647
+ "anchor": "adapter",
648
+ "source": "features/auth-authjs-drizzle/snippets/adapter.ts"
649
+ }
650
+ ]
651
+ },
652
+ {
653
+ "id": "auth-clerk",
654
+ "description": "Clerk provider, middleware and sign-in/up routes. Works with or without a database.",
655
+ "when": {
656
+ "option": "auth",
657
+ "equals": "clerk"
658
+ },
659
+ "dependencies": {
660
+ "@clerk/nextjs": "7.9.17"
661
+ },
662
+ "patches": [
663
+ {
664
+ "op": "env",
665
+ "target": ".env.example",
666
+ "vars": [
667
+ {
668
+ "name": "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY",
669
+ "description": "Clerk publishable key",
670
+ "public": true
671
+ },
672
+ {
673
+ "name": "CLERK_SECRET_KEY",
674
+ "description": "Clerk secret key"
675
+ }
676
+ ]
677
+ },
678
+ {
679
+ "op": "insert",
680
+ "target": "src/app/providers.tsx",
681
+ "anchor": "providers-imports",
682
+ "source": "features/auth-clerk/snippets/providers-imports.tsx"
683
+ },
684
+ {
685
+ "op": "insert",
686
+ "target": "src/app/providers.tsx",
687
+ "anchor": "providers",
688
+ "source": "features/auth-clerk/snippets/providers.tsx"
689
+ }
690
+ ]
691
+ },
692
+ {
693
+ "id": "api-server-actions",
694
+ "description": "Example server action, validation helper and test.",
695
+ "when": {
696
+ "option": "api",
697
+ "equals": "server-actions"
698
+ }
699
+ },
700
+ {
701
+ "id": "api-trpc",
702
+ "description": "tRPC router, context, Next.js route handler and React client provider.",
703
+ "when": {
704
+ "option": "api",
705
+ "equals": "trpc"
706
+ },
707
+ "after": [
708
+ "auth-authjs",
709
+ "auth-clerk"
710
+ ],
711
+ "dependencies": {
712
+ "@trpc/server": "11.19.0",
713
+ "@trpc/client": "11.19.0",
714
+ "@trpc/react-query": "11.19.0",
715
+ "@tanstack/react-query": "5.104.4"
716
+ },
717
+ "replaces": [
718
+ "src/features/notes/components/note-form.tsx"
719
+ ],
720
+ "patches": [
721
+ {
722
+ "op": "insert",
723
+ "target": "src/app/providers.tsx",
724
+ "anchor": "providers-imports",
725
+ "source": "features/api-trpc/snippets/providers-imports.tsx"
726
+ },
727
+ {
728
+ "op": "insert",
729
+ "target": "src/app/providers.tsx",
730
+ "anchor": "providers",
731
+ "source": "features/api-trpc/snippets/providers.tsx",
732
+ "position": "after"
733
+ }
734
+ ]
735
+ },
736
+ {
737
+ "id": "payments-stripe",
738
+ "description": "Stripe checkout route, webhook handler and billing helper. Requires auth and Postgres.",
739
+ "when": {
740
+ "option": "payments",
741
+ "equals": true
742
+ },
743
+ "after": [
744
+ "auth-authjs",
745
+ "auth-clerk",
746
+ "orm-prisma",
747
+ "orm-drizzle"
748
+ ],
749
+ "dependencies": {
750
+ "stripe": "23.0.0"
751
+ },
752
+ "patches": [
753
+ {
754
+ "op": "env",
755
+ "target": ".env.example",
756
+ "vars": [
757
+ {
758
+ "name": "STRIPE_SECRET_KEY",
759
+ "description": "Stripe secret key"
760
+ },
761
+ {
762
+ "name": "STRIPE_WEBHOOK_SECRET",
763
+ "description": "Signing secret for the Stripe webhook endpoint"
764
+ }
765
+ ]
766
+ }
767
+ ]
768
+ },
769
+ {
770
+ "id": "payments-stripe-prisma",
771
+ "description": "Combination overlay: customer/subscription models for Prisma.",
772
+ "when": {
773
+ "all": [
774
+ {
775
+ "option": "payments",
776
+ "equals": true
777
+ },
778
+ {
779
+ "all": [
780
+ {
781
+ "option": "database",
782
+ "equals": "postgres"
783
+ },
784
+ {
785
+ "option": "orm",
786
+ "equals": "prisma"
787
+ }
788
+ ]
789
+ }
790
+ ]
791
+ },
792
+ "after": [
793
+ "payments-stripe",
794
+ "auth-authjs-prisma"
795
+ ],
796
+ "patches": [
797
+ {
798
+ "op": "insert",
799
+ "target": "prisma/schema.prisma",
800
+ "anchor": "models",
801
+ "source": "features/payments-stripe-prisma/snippets/billing-models.prisma",
802
+ "position": "after"
803
+ }
804
+ ]
805
+ },
806
+ {
807
+ "id": "payments-stripe-drizzle",
808
+ "description": "Combination overlay: customer/subscription tables for Drizzle.",
809
+ "when": {
810
+ "all": [
811
+ {
812
+ "option": "payments",
813
+ "equals": true
814
+ },
815
+ {
816
+ "all": [
817
+ {
818
+ "option": "database",
819
+ "equals": "postgres"
820
+ },
821
+ {
822
+ "option": "orm",
823
+ "equals": "drizzle"
824
+ }
825
+ ]
826
+ }
827
+ ]
828
+ },
829
+ "after": [
830
+ "payments-stripe",
831
+ "auth-authjs-drizzle"
832
+ ],
833
+ "patches": [
834
+ {
835
+ "op": "insert",
836
+ "target": "src/server/db/schema.ts",
837
+ "anchor": "tables",
838
+ "source": "features/payments-stripe-drizzle/snippets/billing-tables.ts",
839
+ "position": "after"
840
+ }
841
+ ]
842
+ },
843
+ {
844
+ "id": "deploy-vercel",
845
+ "description": "Vercel deploy notes and README section.",
846
+ "when": {
847
+ "option": "deploy",
848
+ "equals": "vercel"
849
+ },
850
+ "patches": [
851
+ {
852
+ "op": "insert",
853
+ "target": "README.md",
854
+ "anchor": "readme-sections",
855
+ "source": "features/deploy-vercel/snippets/readme-deploy.md",
856
+ "position": "before"
857
+ }
858
+ ],
859
+ "after": [
860
+ "orm-drizzle"
861
+ ]
862
+ },
863
+ {
864
+ "id": "deploy-docker",
865
+ "description": "Dockerfile, .dockerignore, compose file for the app, standalone output.",
866
+ "when": {
867
+ "option": "deploy",
868
+ "equals": "docker"
869
+ },
870
+ "scripts": {
871
+ "image:build": "docker build -t yosr-seed-prototype:gate ."
872
+ },
873
+ "patches": [
874
+ {
875
+ "op": "insert",
876
+ "target": "next.config.ts",
877
+ "anchor": "next-config",
878
+ "content": "...(process.env.YOSR_STANDALONE === \"1\" ? { output: \"standalone\" as const } : {}),"
879
+ }
880
+ ]
881
+ },
882
+ {
883
+ "id": "deploy-docker-npm",
884
+ "description": "Docker install stage for npm: copy package-lock.json and run npm ci.",
885
+ "when": {
886
+ "all": [
887
+ {
888
+ "option": "deploy",
889
+ "equals": "docker"
890
+ },
891
+ {
892
+ "option": "packageManager",
893
+ "equals": "npm"
894
+ }
895
+ ]
896
+ },
897
+ "after": [
898
+ "deploy-docker"
899
+ ],
900
+ "replaces": [
901
+ "Dockerfile"
902
+ ]
903
+ },
904
+ {
905
+ "id": "deploy-docker-postgres",
906
+ "description": "Combination overlay: adds a Postgres service to the compose file.",
907
+ "when": {
908
+ "all": [
909
+ {
910
+ "option": "deploy",
911
+ "equals": "docker"
912
+ },
913
+ {
914
+ "option": "database",
915
+ "equals": "postgres"
916
+ }
917
+ ]
918
+ },
919
+ "after": [
920
+ "deploy-docker"
921
+ ],
922
+ "patches": [
923
+ {
924
+ "op": "insert",
925
+ "target": "compose.yaml",
926
+ "anchor": "services",
927
+ "source": "features/deploy-docker-postgres/snippets/postgres-service.yaml"
928
+ }
929
+ ]
930
+ }
931
+ ],
932
+ "style": {
933
+ "dimensions": [
934
+ {
935
+ "id": "layout",
936
+ "description": "Feature-based (by domain), layer-based (by technical type) or flat folders. Matches style-profile 'layout'.",
937
+ "default": "feature",
938
+ "values": {
939
+ "feature": {
940
+ "pathMap": {
941
+ "@domain": "src/features",
942
+ "@shared": "src/shared",
943
+ "@server": "src/server",
944
+ "@featureUi": "src/features/notes/components"
945
+ },
946
+ "rules": [
947
+ "layout-feature"
948
+ ]
949
+ },
950
+ "layer": {
951
+ "pathMap": {
952
+ "@domain": "src/services",
953
+ "@shared": "src/lib",
954
+ "@server": "src/server",
955
+ "@featureUi": "src/components/notes"
956
+ },
957
+ "rules": [
958
+ "layout-layer"
959
+ ]
960
+ },
961
+ "flat": {
962
+ "pathMap": {
963
+ "@domain": "src/lib",
964
+ "@shared": "src/lib/shared",
965
+ "@server": "src/lib/server",
966
+ "@featureUi": "src/components/notes"
967
+ },
968
+ "rules": [
969
+ "layout-flat"
970
+ ]
971
+ }
972
+ }
973
+ },
974
+ {
975
+ "id": "naming.files",
976
+ "description": "File naming convention for source files.",
977
+ "default": "kebab-case",
978
+ "values": {
979
+ "kebab-case": {},
980
+ "camelCase": {
981
+ "patches": [
982
+ {
983
+ "op": "codemod",
984
+ "codemod": "builtin:rename-files",
985
+ "args": {
986
+ "case": "camel",
987
+ "include": [
988
+ "src/**/*.ts",
989
+ "src/**/*.tsx"
990
+ ],
991
+ "exclude": [
992
+ "src/app/**",
993
+ "src/components/ui/**"
994
+ ]
995
+ }
996
+ }
997
+ ]
998
+ }
999
+ }
1000
+ },
1001
+ {
1002
+ "id": "naming.componentFiles",
1003
+ "description": "React component file names: same casing as other files, or PascalCase.",
1004
+ "default": "same-as-files",
1005
+ "values": {
1006
+ "same-as-files": {},
1007
+ "PascalCase": {
1008
+ "patches": [
1009
+ {
1010
+ "op": "codemod",
1011
+ "codemod": "builtin:rename-files",
1012
+ "args": {
1013
+ "case": "pascal",
1014
+ "include": [
1015
+ "src/components/**/*.tsx",
1016
+ "@domain/**/components/**/*.tsx"
1017
+ ],
1018
+ "exclude": [
1019
+ "src/components/ui/**"
1020
+ ]
1021
+ }
1022
+ }
1023
+ ]
1024
+ }
1025
+ }
1026
+ },
1027
+ {
1028
+ "id": "errors.pattern",
1029
+ "description": "How expected failures are returned from server code.",
1030
+ "default": "throw",
1031
+ "values": {
1032
+ "throw": {
1033
+ "rules": [
1034
+ "errors-throw"
1035
+ ]
1036
+ },
1037
+ "result": {
1038
+ "variantDir": "style/errors-result",
1039
+ "rules": [
1040
+ "errors-result"
1041
+ ]
1042
+ }
1043
+ }
1044
+ },
1045
+ {
1046
+ "id": "data.fetching",
1047
+ "description": "Only server-first is supported by this template in v0.1.",
1048
+ "default": "server-first",
1049
+ "values": {
1050
+ "server-first": {
1051
+ "rules": [
1052
+ "data-server-first"
1053
+ ]
1054
+ }
1055
+ }
1056
+ },
1057
+ {
1058
+ "id": "forms.validation",
1059
+ "description": "Only zod is supported by this template in v0.1.",
1060
+ "default": "zod",
1061
+ "values": {
1062
+ "zod": {
1063
+ "rules": [
1064
+ "forms-zod"
1065
+ ]
1066
+ }
1067
+ }
1068
+ },
1069
+ {
1070
+ "id": "tests.location",
1071
+ "description": "Unit tests next to the code, in adjacent __tests__/ folders, or in a top-level tests/ tree.",
1072
+ "default": "colocated",
1073
+ "values": {
1074
+ "colocated": {
1075
+ "rules": [
1076
+ "tests-colocated"
1077
+ ]
1078
+ },
1079
+ "tests-folder": {
1080
+ "patches": [
1081
+ {
1082
+ "op": "codemod",
1083
+ "codemod": "builtin:move-tests",
1084
+ "args": {
1085
+ "mode": "adjacent-dir",
1086
+ "dir": "__tests__"
1087
+ }
1088
+ }
1089
+ ],
1090
+ "rules": [
1091
+ "tests-folder"
1092
+ ]
1093
+ },
1094
+ "separate": {
1095
+ "patches": [
1096
+ {
1097
+ "op": "codemod",
1098
+ "codemod": "builtin:move-tests",
1099
+ "args": {
1100
+ "to": "tests/unit"
1101
+ }
1102
+ },
1103
+ {
1104
+ "op": "json-merge",
1105
+ "target": "tsconfig.json",
1106
+ "arrays": "append",
1107
+ "value": {
1108
+ "include": [
1109
+ "tests/**/*.ts"
1110
+ ]
1111
+ }
1112
+ }
1113
+ ],
1114
+ "rules": [
1115
+ "tests-separate"
1116
+ ]
1117
+ }
1118
+ }
1119
+ },
1120
+ {
1121
+ "id": "tests.naming",
1122
+ "description": "Unit-test file suffix. .test leaves shipped unit tests unchanged. .spec renames .test. to .spec. under src/ and tests/ only.",
1123
+ "default": ".test",
1124
+ "values": {
1125
+ ".test": {},
1126
+ ".spec": {
1127
+ "patches": [
1128
+ {
1129
+ "op": "codemod",
1130
+ "codemod": "builtin:rename-test-suffix",
1131
+ "args": {
1132
+ "from": ".test.",
1133
+ "to": ".spec."
1134
+ }
1135
+ }
1136
+ ]
1137
+ }
1138
+ }
1139
+ }
1140
+ ]
1141
+ },
1142
+ "enforcement": {
1143
+ "eslint": {
1144
+ "target": "eslint.config.mjs",
1145
+ "anchor": "eslint-style"
1146
+ },
1147
+ "prettier": {
1148
+ "target": ".prettierrc.json"
1149
+ },
1150
+ "tsconfig": {
1151
+ "target": "tsconfig.json"
1152
+ }
1153
+ },
1154
+ "zones": [
1155
+ {
1156
+ "id": "domain-features",
1157
+ "intent": "Domain logic for the user's app: one folder per feature with its server functions, validation schemas and unit tests. Follow the existing example feature. Use the db helper in src/server/db only when a database is configured.",
1158
+ "paths": {
1159
+ "create": [
1160
+ "@domain/**",
1161
+ "@featureUi/**"
1162
+ ],
1163
+ "modify": [
1164
+ "@domain/**",
1165
+ "@featureUi/**"
1166
+ ]
1167
+ },
1168
+ "context": [
1169
+ "@domain/example/**",
1170
+ "AGENTS.md"
1171
+ ],
1172
+ "constraints": {
1173
+ "maxFiles": 40,
1174
+ "allowDependencies": [
1175
+ "zod"
1176
+ ],
1177
+ "forbidPatterns": [
1178
+ "process\\.env\\.[A-Z_]+_SECRET"
1179
+ ]
1180
+ },
1181
+ "tests": {
1182
+ "required": true,
1183
+ "paths": [
1184
+ "@domain/**/*.test.ts"
1185
+ ]
1186
+ }
1187
+ },
1188
+ {
1189
+ "id": "app-pages",
1190
+ "intent": "Pages and layouts for the user's app inside the authenticated app area, composed from shadcn/ui components.",
1191
+ "paths": {
1192
+ "create": [
1193
+ "src/app/(app)/**"
1194
+ ],
1195
+ "modify": [
1196
+ "src/app/(app)/**"
1197
+ ]
1198
+ },
1199
+ "context": [
1200
+ "src/components/ui/**",
1201
+ "src/app/layout.tsx"
1202
+ ],
1203
+ "constraints": {
1204
+ "maxFiles": 30
1205
+ },
1206
+ "tests": {
1207
+ "required": false
1208
+ }
1209
+ },
1210
+ {
1211
+ "id": "db-schema-prisma",
1212
+ "intent": "Add the app's data models to the Prisma schema below the models anchor. Do not change auth models.",
1213
+ "when": {
1214
+ "all": [
1215
+ {
1216
+ "option": "database",
1217
+ "equals": "postgres"
1218
+ },
1219
+ {
1220
+ "option": "orm",
1221
+ "equals": "prisma"
1222
+ }
1223
+ ]
1224
+ },
1225
+ "paths": {
1226
+ "modify": [
1227
+ "prisma/schema.prisma"
1228
+ ]
1229
+ },
1230
+ "constraints": {
1231
+ "maxChangedLines": 200
1232
+ },
1233
+ "tests": {
1234
+ "required": false
1235
+ }
1236
+ },
1237
+ {
1238
+ "id": "db-schema-drizzle",
1239
+ "intent": "Add the app's tables to the Drizzle schema. Do not change auth tables.",
1240
+ "when": {
1241
+ "all": [
1242
+ {
1243
+ "option": "database",
1244
+ "equals": "postgres"
1245
+ },
1246
+ {
1247
+ "option": "orm",
1248
+ "equals": "drizzle"
1249
+ }
1250
+ ]
1251
+ },
1252
+ "paths": {
1253
+ "modify": [
1254
+ "src/server/db/schema.ts"
1255
+ ]
1256
+ },
1257
+ "constraints": {
1258
+ "maxChangedLines": 200
1259
+ },
1260
+ "tests": {
1261
+ "required": false
1262
+ }
1263
+ }
1264
+ ],
1265
+ "protected": [
1266
+ "src/server/auth/**",
1267
+ "src/middleware.ts",
1268
+ "src/proxy.ts",
1269
+ "src/app/api/webhooks/**",
1270
+ ".github/**"
1271
+ ],
1272
+ "rules": {
1273
+ "targets": [
1274
+ "agents-md",
1275
+ "claude-md",
1276
+ "cursor-rules"
1277
+ ],
1278
+ "fragments": [
1279
+ {
1280
+ "id": "project-overview",
1281
+ "file": "rules/project-overview.md",
1282
+ "title": "Project overview",
1283
+ "order": 10,
1284
+ "cursor": {
1285
+ "alwaysApply": true,
1286
+ "description": "What this project is and how it is laid out"
1287
+ }
1288
+ },
1289
+ {
1290
+ "id": "commands",
1291
+ "file": "rules/commands.md",
1292
+ "title": "Commands",
1293
+ "order": 20,
1294
+ "cursor": {
1295
+ "alwaysApply": true
1296
+ }
1297
+ },
1298
+ {
1299
+ "id": "layout-feature",
1300
+ "file": "rules/layout-feature.md",
1301
+ "title": "Folder layout",
1302
+ "order": 30,
1303
+ "cursor": {
1304
+ "globs": [
1305
+ "src/**"
1306
+ ]
1307
+ }
1308
+ },
1309
+ {
1310
+ "id": "layout-layer",
1311
+ "file": "rules/layout-layer.md",
1312
+ "title": "Folder layout",
1313
+ "order": 30,
1314
+ "cursor": {
1315
+ "globs": [
1316
+ "src/**"
1317
+ ]
1318
+ }
1319
+ },
1320
+ {
1321
+ "id": "layout-flat",
1322
+ "file": "rules/layout-flat.md",
1323
+ "title": "Folder layout",
1324
+ "order": 30,
1325
+ "cursor": {
1326
+ "globs": [
1327
+ "src/**"
1328
+ ]
1329
+ }
1330
+ },
1331
+ {
1332
+ "id": "errors-throw",
1333
+ "file": "rules/errors-throw.md",
1334
+ "title": "Error handling",
1335
+ "order": 40,
1336
+ "cursor": {
1337
+ "globs": [
1338
+ "src/server/**",
1339
+ "@domain/**"
1340
+ ]
1341
+ }
1342
+ },
1343
+ {
1344
+ "id": "errors-result",
1345
+ "file": "rules/errors-result.md",
1346
+ "title": "Error handling",
1347
+ "order": 40,
1348
+ "cursor": {
1349
+ "globs": [
1350
+ "src/server/**",
1351
+ "@domain/**"
1352
+ ]
1353
+ }
1354
+ },
1355
+ {
1356
+ "id": "data-server-first",
1357
+ "file": "rules/data-server-first.md",
1358
+ "title": "Data fetching",
1359
+ "order": 45
1360
+ },
1361
+ {
1362
+ "id": "forms-zod",
1363
+ "file": "rules/forms-zod.md",
1364
+ "title": "Forms and validation",
1365
+ "order": 46
1366
+ },
1367
+ {
1368
+ "id": "tests-colocated",
1369
+ "file": "rules/tests-colocated.md",
1370
+ "title": "Testing",
1371
+ "order": 50,
1372
+ "cursor": {
1373
+ "globs": [
1374
+ "**/*.test.ts",
1375
+ "**/*.test.tsx",
1376
+ "**/*.spec.ts",
1377
+ "**/*.spec.tsx"
1378
+ ]
1379
+ }
1380
+ },
1381
+ {
1382
+ "id": "tests-folder",
1383
+ "file": "rules/tests-folder.md",
1384
+ "title": "Testing",
1385
+ "order": 50,
1386
+ "cursor": {
1387
+ "globs": [
1388
+ "**/__tests__/**"
1389
+ ]
1390
+ }
1391
+ },
1392
+ {
1393
+ "id": "tests-separate",
1394
+ "file": "rules/tests-separate.md",
1395
+ "title": "Testing",
1396
+ "order": 50,
1397
+ "cursor": {
1398
+ "globs": [
1399
+ "tests/**"
1400
+ ]
1401
+ }
1402
+ },
1403
+ {
1404
+ "id": "auth-authjs",
1405
+ "file": "rules/auth-authjs.md",
1406
+ "title": "Authentication (Auth.js)",
1407
+ "order": 60,
1408
+ "when": {
1409
+ "option": "auth",
1410
+ "equals": "authjs"
1411
+ }
1412
+ },
1413
+ {
1414
+ "id": "auth-clerk",
1415
+ "file": "rules/auth-clerk.md",
1416
+ "title": "Authentication (Clerk)",
1417
+ "order": 60,
1418
+ "when": {
1419
+ "option": "auth",
1420
+ "equals": "clerk"
1421
+ }
1422
+ },
1423
+ {
1424
+ "id": "data-prisma",
1425
+ "file": "rules/data-prisma.md",
1426
+ "title": "Data access (Prisma)",
1427
+ "order": 70,
1428
+ "when": {
1429
+ "all": [
1430
+ {
1431
+ "option": "database",
1432
+ "equals": "postgres"
1433
+ },
1434
+ {
1435
+ "option": "orm",
1436
+ "equals": "prisma"
1437
+ }
1438
+ ]
1439
+ },
1440
+ "cursor": {
1441
+ "globs": [
1442
+ "prisma/**",
1443
+ "src/server/db/**"
1444
+ ]
1445
+ }
1446
+ },
1447
+ {
1448
+ "id": "data-drizzle",
1449
+ "file": "rules/data-drizzle.md",
1450
+ "title": "Data access (Drizzle)",
1451
+ "order": 70,
1452
+ "when": {
1453
+ "all": [
1454
+ {
1455
+ "option": "database",
1456
+ "equals": "postgres"
1457
+ },
1458
+ {
1459
+ "option": "orm",
1460
+ "equals": "drizzle"
1461
+ }
1462
+ ]
1463
+ },
1464
+ "cursor": {
1465
+ "globs": [
1466
+ "src/server/db/**"
1467
+ ]
1468
+ }
1469
+ },
1470
+ {
1471
+ "id": "data-none",
1472
+ "file": "rules/data-none.md",
1473
+ "title": "Data (no database)",
1474
+ "order": 70,
1475
+ "when": {
1476
+ "option": "database",
1477
+ "equals": "none"
1478
+ }
1479
+ },
1480
+ {
1481
+ "id": "api-server-actions",
1482
+ "file": "rules/api-server-actions.md",
1483
+ "title": "API (server actions)",
1484
+ "order": 80,
1485
+ "when": {
1486
+ "option": "api",
1487
+ "equals": "server-actions"
1488
+ }
1489
+ },
1490
+ {
1491
+ "id": "api-trpc",
1492
+ "file": "rules/api-trpc.md",
1493
+ "title": "API (tRPC)",
1494
+ "order": 80,
1495
+ "when": {
1496
+ "option": "api",
1497
+ "equals": "trpc"
1498
+ },
1499
+ "cursor": {
1500
+ "globs": [
1501
+ "src/server/api/**"
1502
+ ]
1503
+ }
1504
+ },
1505
+ {
1506
+ "id": "payments-stripe",
1507
+ "file": "rules/payments-stripe.md",
1508
+ "title": "Payments (Stripe)",
1509
+ "order": 90,
1510
+ "when": {
1511
+ "option": "payments",
1512
+ "equals": true
1513
+ }
1514
+ },
1515
+ {
1516
+ "id": "ui-shadcn",
1517
+ "file": "rules/ui-shadcn.md",
1518
+ "title": "UI components",
1519
+ "order": 100,
1520
+ "cursor": {
1521
+ "globs": [
1522
+ "src/components/**",
1523
+ "src/app/**/*.tsx"
1524
+ ]
1525
+ }
1526
+ }
1527
+ ],
1528
+ "architectureNote": {
1529
+ "source": "rules/architecture.md",
1530
+ "target": "docs/architecture.md"
1531
+ }
1532
+ },
1533
+ "checks": {
1534
+ "maxFixAttempts": 3,
1535
+ "services": [
1536
+ {
1537
+ "id": "postgres",
1538
+ "image": "postgres:18.6-alpine",
1539
+ "env": {
1540
+ "POSTGRES_PASSWORD": "postgres"
1541
+ },
1542
+ "when": {
1543
+ "option": "database",
1544
+ "equals": "postgres"
1545
+ }
1546
+ }
1547
+ ],
1548
+ "steps": [
1549
+ {
1550
+ "id": "install",
1551
+ "kind": "install",
1552
+ "run": {
1553
+ "install": true
1554
+ },
1555
+ "timeoutSec": 600
1556
+ },
1557
+ {
1558
+ "id": "lint",
1559
+ "kind": "lint",
1560
+ "run": {
1561
+ "script": "lint"
1562
+ },
1563
+ "timeoutSec": 300
1564
+ },
1565
+ {
1566
+ "id": "typecheck",
1567
+ "kind": "typecheck",
1568
+ "run": {
1569
+ "script": "typecheck"
1570
+ },
1571
+ "timeoutSec": 300
1572
+ },
1573
+ {
1574
+ "id": "build",
1575
+ "kind": "build",
1576
+ "run": {
1577
+ "script": "build"
1578
+ },
1579
+ "timeoutSec": 900,
1580
+ "env": {
1581
+ "SKIP_ENV_VALIDATION": "1"
1582
+ }
1583
+ },
1584
+ {
1585
+ "id": "test",
1586
+ "kind": "test",
1587
+ "run": {
1588
+ "script": "test"
1589
+ },
1590
+ "timeoutSec": 600
1591
+ },
1592
+ {
1593
+ "id": "image",
1594
+ "kind": "custom",
1595
+ "run": {
1596
+ "script": "image:build"
1597
+ },
1598
+ "timeoutSec": 900,
1599
+ "required": false,
1600
+ "when": {
1601
+ "option": "deploy",
1602
+ "equals": "docker"
1603
+ },
1604
+ "phases": [
1605
+ "handoff",
1606
+ "template-ci",
1607
+ "project-ci"
1608
+ ]
1609
+ },
1610
+ {
1611
+ "id": "db-setup",
1612
+ "kind": "custom",
1613
+ "run": {
1614
+ "script": "db:push"
1615
+ },
1616
+ "timeoutSec": 120,
1617
+ "required": false,
1618
+ "when": {
1619
+ "option": "database",
1620
+ "equals": "postgres"
1621
+ },
1622
+ "phases": [
1623
+ "handoff",
1624
+ "template-ci",
1625
+ "project-ci"
1626
+ ]
1627
+ },
1628
+ {
1629
+ "id": "e2e",
1630
+ "kind": "e2e",
1631
+ "run": {
1632
+ "script": "test:e2e"
1633
+ },
1634
+ "timeoutSec": 900,
1635
+ "required": false,
1636
+ "phases": [
1637
+ "handoff",
1638
+ "template-ci",
1639
+ "project-ci"
1640
+ ]
1641
+ }
1642
+ ],
1643
+ "report": {
1644
+ "path": ".seed/report.md"
1645
+ }
1646
+ },
1647
+ "matrix": {
1648
+ "strategy": "pairwise",
1649
+ "packageManagers": [
1650
+ "pnpm",
1651
+ "npm"
1652
+ ],
1653
+ "styleDimensions": [
1654
+ "layout",
1655
+ "errors.pattern",
1656
+ "tests.location"
1657
+ ],
1658
+ "include": [
1659
+ {
1660
+ "name": "defaults",
1661
+ "options": {}
1662
+ },
1663
+ {
1664
+ "name": "defaults-npm",
1665
+ "options": {},
1666
+ "packageManager": "npm"
1667
+ },
1668
+ {
1669
+ "name": "minimal",
1670
+ "options": {
1671
+ "database": "none",
1672
+ "auth": "none",
1673
+ "api": "server-actions",
1674
+ "deploy": "vercel"
1675
+ }
1676
+ },
1677
+ {
1678
+ "name": "no-db-clerk",
1679
+ "options": {
1680
+ "database": "none",
1681
+ "auth": "clerk",
1682
+ "api": "trpc",
1683
+ "deploy": "docker"
1684
+ }
1685
+ },
1686
+ {
1687
+ "name": "kitchen-sink",
1688
+ "options": {
1689
+ "database": "postgres",
1690
+ "orm": "prisma",
1691
+ "auth": "authjs",
1692
+ "api": "trpc",
1693
+ "payments": true,
1694
+ "deploy": "docker"
1695
+ },
1696
+ "style": {
1697
+ "layout": "layer",
1698
+ "naming.files": "camelCase",
1699
+ "naming.componentFiles": "PascalCase",
1700
+ "errors.pattern": "result",
1701
+ "tests.location": "tests-folder"
1702
+ }
1703
+ },
1704
+ {
1705
+ "name": "minimal-preset",
1706
+ "options": {
1707
+ "database": "none"
1708
+ },
1709
+ "style": {
1710
+ "layout": "flat"
1711
+ }
1712
+ }
1713
+ ]
1714
+ },
1715
+ "migrations": [],
1716
+ "x-prototype": {
1717
+ "status": "prototype, not published",
1718
+ "stubbedFeatures": [],
1719
+ "notes": "No features are stubbed. The prototype renderer still refuses any id listed in stubbedFeatures."
1720
+ }
1721
+ }