create-m5kdev 0.41.0 → 0.41.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 (61) hide show
  1. package/package.json +1 -1
  2. package/templates/minimal-app/.cursor/rules/frontend-component-guide.mdc +95 -95
  3. package/templates/minimal-app/.cursor/rules/frontend-data-guide.mdc +103 -103
  4. package/templates/minimal-app/.cursor/rules/frontend-form-guide.mdc +80 -80
  5. package/templates/minimal-app/.cursor/rules/frontend-hook-guide.mdc +79 -79
  6. package/templates/minimal-app/.cursor/rules/frontend-i18n-guide.mdc +45 -45
  7. package/templates/minimal-app/.cursor/rules/module-dto-guide.mdc +60 -60
  8. package/templates/minimal-app/.cursor/rules/module-prompt-guide.mdc +214 -214
  9. package/templates/minimal-app/.cursor/rules/module-repository-guide.mdc +167 -167
  10. package/templates/minimal-app/.cursor/rules/module-schema-guide.mdc +53 -53
  11. package/templates/minimal-app/.cursor/rules/module-service-guide.mdc +367 -367
  12. package/templates/minimal-app/.cursor/rules/module-trpc-guide.mdc +71 -71
  13. package/templates/minimal-app/.github/workflows/check.yml.tpl +21 -0
  14. package/templates/minimal-app/AGENTS.md.tpl +22 -31
  15. package/templates/minimal-app/CLAUDE.md.tpl +15 -1
  16. package/templates/minimal-app/GLOSSARY.md.tpl +181 -0
  17. package/templates/minimal-app/README.md.tpl +2 -0
  18. package/templates/minimal-app/apps/.gitignore.tpl +13 -13
  19. package/templates/minimal-app/apps/e2e/package.json.tpl +23 -23
  20. package/templates/minimal-app/apps/email/package.json.tpl +34 -34
  21. package/templates/minimal-app/apps/expo/.gitignore.tpl +44 -44
  22. package/templates/minimal-app/apps/expo/.prettierignore +13 -13
  23. package/templates/minimal-app/apps/expo/.prettierrc.json.tpl +9 -9
  24. package/templates/minimal-app/apps/expo/CHANGELOG.md.tpl +156 -156
  25. package/templates/minimal-app/apps/expo/README.md.tpl +44 -44
  26. package/templates/minimal-app/apps/expo/expo-env.d.ts.tpl +3 -0
  27. package/templates/minimal-app/apps/expo/package.json.tpl +63 -63
  28. package/templates/minimal-app/apps/expo/uniwind-types.d.ts.tpl +10 -0
  29. package/templates/minimal-app/apps/landing/package.json.tpl +34 -34
  30. package/templates/minimal-app/apps/server/drizzle/0000_whole_echo.sql +366 -366
  31. package/templates/minimal-app/apps/server/drizzle/0002_crazy_lilith.sql +46 -46
  32. package/templates/minimal-app/apps/server/drizzle/0003_freezing_fantastic_four.sql +3 -3
  33. package/templates/minimal-app/apps/server/drizzle/0004_slow_ezekiel_stane.sql +98 -98
  34. package/templates/minimal-app/apps/server/drizzle/0005_wonderful_the_professor.sql +1 -1
  35. package/templates/minimal-app/apps/server/drizzle/0006_lazy_blue_marvel.sql +158 -158
  36. package/templates/minimal-app/apps/server/drizzle/0007_abnormal_ultimo.sql +1 -1
  37. package/templates/minimal-app/apps/server/drizzle/0008_steady_nekra.sql +86 -86
  38. package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +2624 -2624
  39. package/templates/minimal-app/apps/server/drizzle/meta/0001_snapshot.json.tpl +2670 -2670
  40. package/templates/minimal-app/apps/server/drizzle/meta/0002_snapshot.json.tpl +2715 -2715
  41. package/templates/minimal-app/apps/server/drizzle/meta/0003_snapshot.json.tpl +2687 -2687
  42. package/templates/minimal-app/apps/server/drizzle/meta/0004_snapshot.json.tpl +2497 -2497
  43. package/templates/minimal-app/apps/server/drizzle/meta/0005_snapshot.json.tpl +2483 -2483
  44. package/templates/minimal-app/apps/server/drizzle/meta/0006_snapshot.json.tpl +3602 -3602
  45. package/templates/minimal-app/apps/server/drizzle/meta/0007_snapshot.json.tpl +3619 -3619
  46. package/templates/minimal-app/apps/server/drizzle/meta/0008_snapshot.json.tpl +3619 -3619
  47. package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +76 -76
  48. package/templates/minimal-app/apps/server/package.json.tpl +57 -57
  49. package/templates/minimal-app/apps/server/src/app.ts.tpl +1 -0
  50. package/templates/minimal-app/apps/shared/package.json.tpl +27 -27
  51. package/templates/minimal-app/apps/webapp/AGENTS.md.tpl +24 -24
  52. package/templates/minimal-app/apps/webapp/index.html.tpl +22 -22
  53. package/templates/minimal-app/apps/webapp/package.json.tpl +53 -53
  54. package/templates/minimal-app/apps/webapp/public/logo.svg.tpl +6 -6
  55. package/templates/minimal-app/biome.json.tpl +1 -0
  56. package/templates/minimal-app/docs/adr/.gitkeep +0 -0
  57. package/templates/minimal-app/docs/agents/domain.md.tpl +38 -0
  58. package/templates/minimal-app/docs/agents/issue-tracker.md.tpl +45 -0
  59. package/templates/minimal-app/docs/agents/triage-labels.md.tpl +15 -0
  60. package/templates/minimal-app/pnpm-workspace.yaml.tpl +12 -12
  61. package/templates/minimal-app/template.manifest.json +12 -0
@@ -1,79 +1,79 @@
1
- ---
2
- globs: apps/**/webapp/src/**/hooks/**
3
- alwaysApply: false
4
- ---
5
-
6
- # Frontend Custom Hook Style Guide
7
-
8
- Feature logic lives in custom hooks so components stay display-focused.
9
-
10
- ## When to create a hook
11
-
12
- Create a custom hook when a component accumulates real logic: multiple
13
- `useQuery`/`useMutation` calls, `queryClient` invalidation, nuqs URL state,
14
- or `useEffect` orchestration. A component that only needs one small hook and
15
- some ephemeral UI state does not need a wrapper — this rule is about keeping
16
- the JSX editable, not about ceremony.
17
-
18
- ## Structure
19
-
20
- - Location: `src/modules/<feature>/hooks/use<Name>.ts`
21
- - Compose small, focused hooks into one screen-level hook so the route
22
- component makes a single call:
23
-
24
- ```
25
- modules/posts/hooks/
26
- usePostsList.ts // list query + URL state (search, status, page)
27
- usePostActions.ts // reusable mutations: save, publish, delete
28
- usePostsRoute.ts // composes both for the PostsRoute screen
29
- ```
30
-
31
- ```ts
32
- export function usePostsRoute() {
33
- const list = usePostsList();
34
- const actions = usePostActions();
35
- return { ...list, ...actions };
36
- }
37
- ```
38
-
39
- ## Reusable actions get their own hook
40
-
41
- Mutations that more than one component may trigger (delete, publish, archive)
42
- are always extracted into their own hook — never inlined into a screen hook —
43
- so other components can reuse them with the same toasts, confirmation dialogs,
44
- and invalidation.
45
-
46
- ## What a hook owns
47
-
48
- - tRPC queries/mutations and their `queryFilter()` invalidation.
49
- - nuqs URL state (filters, pagination, tabs) and its update functions
50
- (wrap updates in `startTransition`).
51
- - Derived data (`useMemo`): rows, totals, page counts, stats.
52
- - Side effects of actions: success/error toasts, confirmation dialogs
53
- (`useDialog`), cache invalidation.
54
- - Pending indicators: booleans (`isSaving`) or row-scoped ids
55
- (`deletingId`) derived from `mutation.isPending` + `mutation.variables`.
56
-
57
- ## Return shape
58
-
59
- Return a flat object of data, derived values, pending flags, and action
60
- functions. Action functions used by forms should resolve to a success boolean
61
- instead of throwing, so components can close dialogs without try/catch:
62
-
63
- ```ts
64
- const savePost = async (payload: PostCreateInputSchema, id?: string): Promise<boolean> => {
65
- try {
66
- if (id) await updateMutation.mutateAsync({ id, ...payload });
67
- else await createMutation.mutateAsync(payload);
68
- return true;
69
- } catch {
70
- return false; // the mutation's onError already showed a toast
71
- }
72
- };
73
- ```
74
-
75
- ## What a hook must not do
76
-
77
- - Return JSX or take React nodes as arguments.
78
- - Read DOM elements directly (the submit handler that parses `FormData`
79
- stays in the form component — see the frontend form guide).
1
+ ---
2
+ globs: apps/**/webapp/src/**/hooks/**
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # Frontend Custom Hook Style Guide
7
+
8
+ Feature logic lives in custom hooks so components stay display-focused.
9
+
10
+ ## When to create a hook
11
+
12
+ Create a custom hook when a component accumulates real logic: multiple
13
+ `useQuery`/`useMutation` calls, `queryClient` invalidation, nuqs URL state,
14
+ or `useEffect` orchestration. A component that only needs one small hook and
15
+ some ephemeral UI state does not need a wrapper — this rule is about keeping
16
+ the JSX editable, not about ceremony.
17
+
18
+ ## Structure
19
+
20
+ - Location: `src/modules/<feature>/hooks/use<Name>.ts`
21
+ - Compose small, focused hooks into one screen-level hook so the route
22
+ component makes a single call:
23
+
24
+ ```
25
+ modules/posts/hooks/
26
+ usePostsList.ts // list query + URL state (search, status, page)
27
+ usePostActions.ts // reusable mutations: save, publish, delete
28
+ usePostsRoute.ts // composes both for the PostsRoute screen
29
+ ```
30
+
31
+ ```ts
32
+ export function usePostsRoute() {
33
+ const list = usePostsList();
34
+ const actions = usePostActions();
35
+ return { ...list, ...actions };
36
+ }
37
+ ```
38
+
39
+ ## Reusable actions get their own hook
40
+
41
+ Mutations that more than one component may trigger (delete, publish, archive)
42
+ are always extracted into their own hook — never inlined into a screen hook —
43
+ so other components can reuse them with the same toasts, confirmation dialogs,
44
+ and invalidation.
45
+
46
+ ## What a hook owns
47
+
48
+ - tRPC queries/mutations and their `queryFilter()` invalidation.
49
+ - nuqs URL state (filters, pagination, tabs) and its update functions
50
+ (wrap updates in `startTransition`).
51
+ - Derived data (`useMemo`): rows, totals, page counts, stats.
52
+ - Side effects of actions: success/error toasts, confirmation dialogs
53
+ (`useDialog`), cache invalidation.
54
+ - Pending indicators: booleans (`isSaving`) or row-scoped ids
55
+ (`deletingId`) derived from `mutation.isPending` + `mutation.variables`.
56
+
57
+ ## Return shape
58
+
59
+ Return a flat object of data, derived values, pending flags, and action
60
+ functions. Action functions used by forms should resolve to a success boolean
61
+ instead of throwing, so components can close dialogs without try/catch:
62
+
63
+ ```ts
64
+ const savePost = async (payload: PostCreateInputSchema, id?: string): Promise<boolean> => {
65
+ try {
66
+ if (id) await updateMutation.mutateAsync({ id, ...payload });
67
+ else await createMutation.mutateAsync(payload);
68
+ return true;
69
+ } catch {
70
+ return false; // the mutation's onError already showed a toast
71
+ }
72
+ };
73
+ ```
74
+
75
+ ## What a hook must not do
76
+
77
+ - Return JSX or take React nodes as arguments.
78
+ - Read DOM elements directly (the submit handler that parses `FormData`
79
+ stays in the form component — see the frontend form guide).
@@ -1,45 +1,45 @@
1
- ---
2
- globs: apps/**/webapp/src/**/*.tsx
3
- alwaysApply: false
4
- ---
5
-
6
- # Frontend i18n Style Guide
7
-
8
- All user-facing copy goes through i18next. No hardcoded strings in JSX,
9
- toasts, aria-labels, or placeholders.
10
-
11
- ## Usage
12
-
13
- ```tsx
14
- const { t } = useTranslation("<app-namespace>");
15
- <h2>{t("posts.hero.title")}</h2>
16
- <Input placeholder={t("posts.filters.searchPlaceholder")} aria-label={t("posts.filters.searchLabel")} />
17
- toast.success(t("posts.toast.created"));
18
- ```
19
-
20
- - `useTranslation(...)` is one of the few hooks always allowed directly inside
21
- display components.
22
- - Interpolate variables through i18next, never string concatenation:
23
- `t("posts.pagination.summary", { page, pageCount })`.
24
-
25
- ## Where keys live
26
-
27
- - App copy: `apps/webapp/translations/en/<namespace>.json` — add the key in
28
- the same change that uses it.
29
- - Role labels and app-level chrome: `apps/webapp/translations/app.json`
30
- (resolved as `app:...`, e.g. `app:organization.role.editor`).
31
- - Auth/billing route copy ships with `@m5kdev/web-ui` (`web-ui:` namespace) —
32
- do not duplicate it.
33
-
34
- ## Key conventions
35
-
36
- - Structure keys as `<feature>.<section>.<name>`:
37
- `posts.editor.fields.title`, `posts.toast.created`, `layout.push.cta`.
38
- - Reuse existing keys before adding near-duplicates.
39
-
40
- ## Server errors
41
-
42
- Backend `ServerError` responses carry i18n-style client messages
43
- (`server.error.<layer>.<code>`). When surfacing mutation errors, prefer a
44
- translated, human message; fall back to `error.message` only when no
45
- translation exists.
1
+ ---
2
+ globs: apps/**/webapp/src/**/*.tsx
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # Frontend i18n Style Guide
7
+
8
+ All user-facing copy goes through i18next. No hardcoded strings in JSX,
9
+ toasts, aria-labels, or placeholders.
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ const { t } = useTranslation("<app-namespace>");
15
+ <h2>{t("posts.hero.title")}</h2>
16
+ <Input placeholder={t("posts.filters.searchPlaceholder")} aria-label={t("posts.filters.searchLabel")} />
17
+ toast.success(t("posts.toast.created"));
18
+ ```
19
+
20
+ - `useTranslation(...)` is one of the few hooks always allowed directly inside
21
+ display components.
22
+ - Interpolate variables through i18next, never string concatenation:
23
+ `t("posts.pagination.summary", { page, pageCount })`.
24
+
25
+ ## Where keys live
26
+
27
+ - App copy: `apps/webapp/translations/en/<namespace>.json` — add the key in
28
+ the same change that uses it.
29
+ - Role labels and app-level chrome: `apps/webapp/translations/app.json`
30
+ (resolved as `app:...`, e.g. `app:organization.role.editor`).
31
+ - Auth/billing route copy ships with `@m5kdev/web-ui` (`web-ui:` namespace) —
32
+ do not duplicate it.
33
+
34
+ ## Key conventions
35
+
36
+ - Structure keys as `<feature>.<section>.<name>`:
37
+ `posts.editor.fields.title`, `posts.toast.created`, `layout.push.cta`.
38
+ - Reuse existing keys before adding near-duplicates.
39
+
40
+ ## Server errors
41
+
42
+ Backend `ServerError` responses carry i18n-style client messages
43
+ (`server.error.<layer>.<code>`). When surfacing mutation errors, prefer a
44
+ translated, human message; fall back to `error.message` only when no
45
+ translation exists.
@@ -1,60 +1,60 @@
1
- ---
2
- globs: apps/**/server/src/modules/**/*.dto.ts
3
- alwaysApply: false
4
- ---
5
-
6
- # Module DTO Style Guide
7
-
8
- The DTO is the module's API surface, **derived from the Drizzle table** —
9
- database schema first. Services, repositories, and tRPC files import the DTO
10
- *object* (`postSchemas`) and reference `postSchemas.input.x` /
11
- `postSchemas.output.y`; they do not import hand-written types.
12
-
13
- ## The pattern
14
-
15
- ```ts
16
- import { createZodSchemas } from "@m5kdev/backend/base/base.dto";
17
- import {
18
- postCreateInputSchema,
19
- postUpdateInputSchema,
20
- } from "{{PACKAGE_SCOPE}}/shared/modules/posts/posts.schema";
21
- import { z } from "zod";
22
- import { posts } from "./posts.db";
23
-
24
- const { output, input } = createZodSchemas(posts);
25
-
26
- export const postSchemas = {
27
- output,
28
- input: {
29
- ...input,
30
- // create/update come from shared: the same schemas validate the webapp
31
- // form, so the API contract and frontend validation cannot drift
32
- create: postCreateInputSchema,
33
- update: postUpdateInputSchema,
34
- publish: z.object({ id: z.string() }),
35
- },
36
- };
37
- ```
38
-
39
- ## What `createZodSchemas(table)` provides
40
-
41
- - `output.single` — the row (from `createSelectSchema`).
42
- - `output.found` — `single.optional()`, for find-style queries that may
43
- return nothing.
44
- - `output.list` — `{ rows, total }` envelope.
45
- - `output.uuid` / `output.uuids` — id acknowledgements.
46
- - `input.list` — the generic `querySchema` (pagination, sorting, `q`,
47
- `filters`).
48
- - `input.read/readMany/delete/deleteMany` — id inputs.
49
- - `input.create` / `input.update` — raw insert/update schemas from the table.
50
-
51
- ## Rules
52
-
53
- - **Always override `input.create` and `input.update`** with narrower schemas:
54
- the raw insert schema exposes server-managed columns (`id`, timestamps,
55
- owner ids) that clients must never send. If the frontend validates the same
56
- form, import the schema from shared instead of redefining it.
57
- - Add action-specific inputs (`publish`, `archive`) as plain Zod objects here.
58
- - Extend `input.list` only when a custom list contract is genuinely needed;
59
- prefer the generic `q`/`filters` contract (see repository guide).
60
- - One exported `<module>Schemas` object per module — no loose schema exports.
1
+ ---
2
+ globs: apps/**/server/src/modules/**/*.dto.ts
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # Module DTO Style Guide
7
+
8
+ The DTO is the module's API surface, **derived from the Drizzle table** —
9
+ database schema first. Services, repositories, and tRPC files import the DTO
10
+ *object* (`postSchemas`) and reference `postSchemas.input.x` /
11
+ `postSchemas.output.y`; they do not import hand-written types.
12
+
13
+ ## The pattern
14
+
15
+ ```ts
16
+ import { createZodSchemas } from "@m5kdev/backend/base/base.dto";
17
+ import {
18
+ postCreateInputSchema,
19
+ postUpdateInputSchema,
20
+ } from "{{PACKAGE_SCOPE}}/shared/modules/posts/posts.schema";
21
+ import { z } from "zod";
22
+ import { posts } from "./posts.db";
23
+
24
+ const { output, input } = createZodSchemas(posts);
25
+
26
+ export const postSchemas = {
27
+ output,
28
+ input: {
29
+ ...input,
30
+ // create/update come from shared: the same schemas validate the webapp
31
+ // form, so the API contract and frontend validation cannot drift
32
+ create: postCreateInputSchema,
33
+ update: postUpdateInputSchema,
34
+ publish: z.object({ id: z.string() }),
35
+ },
36
+ };
37
+ ```
38
+
39
+ ## What `createZodSchemas(table)` provides
40
+
41
+ - `output.single` — the row (from `createSelectSchema`).
42
+ - `output.found` — `single.optional()`, for find-style queries that may
43
+ return nothing.
44
+ - `output.list` — `{ rows, total }` envelope.
45
+ - `output.uuid` / `output.uuids` — id acknowledgements.
46
+ - `input.list` — the generic `querySchema` (pagination, sorting, `q`,
47
+ `filters`).
48
+ - `input.read/readMany/delete/deleteMany` — id inputs.
49
+ - `input.create` / `input.update` — raw insert/update schemas from the table.
50
+
51
+ ## Rules
52
+
53
+ - **Always override `input.create` and `input.update`** with narrower schemas:
54
+ the raw insert schema exposes server-managed columns (`id`, timestamps,
55
+ owner ids) that clients must never send. If the frontend validates the same
56
+ form, import the schema from shared instead of redefining it.
57
+ - Add action-specific inputs (`publish`, `archive`) as plain Zod objects here.
58
+ - Extend `input.list` only when a custom list contract is genuinely needed;
59
+ prefer the generic `q`/`filters` contract (see repository guide).
60
+ - One exported `<module>Schemas` object per module — no loose schema exports.