create-m5kdev 0.40.3 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/minimal-app/.cursor/rules/frontend-component-guide.mdc +95 -95
- package/templates/minimal-app/.cursor/rules/frontend-data-guide.mdc +103 -103
- package/templates/minimal-app/.cursor/rules/frontend-form-guide.mdc +80 -80
- package/templates/minimal-app/.cursor/rules/frontend-hook-guide.mdc +79 -79
- package/templates/minimal-app/.cursor/rules/frontend-i18n-guide.mdc +45 -45
- package/templates/minimal-app/.cursor/rules/module-dto-guide.mdc +60 -60
- package/templates/minimal-app/.cursor/rules/module-prompt-guide.mdc +214 -214
- package/templates/minimal-app/.cursor/rules/module-repository-guide.mdc +167 -167
- package/templates/minimal-app/.cursor/rules/module-schema-guide.mdc +53 -53
- package/templates/minimal-app/.cursor/rules/module-service-guide.mdc +367 -367
- package/templates/minimal-app/.cursor/rules/module-trpc-guide.mdc +71 -71
- package/templates/minimal-app/apps/.gitignore.tpl +13 -13
- package/templates/minimal-app/apps/e2e/package.json.tpl +23 -23
- package/templates/minimal-app/apps/e2e/tests/standard.spec.ts.tpl +11 -22
- package/templates/minimal-app/apps/email/package.json.tpl +34 -34
- package/templates/minimal-app/apps/expo/.gitignore.tpl +44 -44
- package/templates/minimal-app/apps/expo/.prettierignore +13 -13
- package/templates/minimal-app/apps/expo/.prettierrc.json.tpl +9 -9
- package/templates/minimal-app/apps/expo/CHANGELOG.md.tpl +156 -156
- package/templates/minimal-app/apps/expo/README.md.tpl +44 -44
- package/templates/minimal-app/apps/expo/package.json.tpl +63 -63
- package/templates/minimal-app/apps/landing/package.json.tpl +34 -34
- package/templates/minimal-app/apps/server/drizzle/0000_whole_echo.sql +366 -366
- package/templates/minimal-app/apps/server/drizzle/0002_crazy_lilith.sql +46 -46
- package/templates/minimal-app/apps/server/drizzle/0003_freezing_fantastic_four.sql +3 -3
- package/templates/minimal-app/apps/server/drizzle/0004_slow_ezekiel_stane.sql +98 -98
- package/templates/minimal-app/apps/server/drizzle/0005_wonderful_the_professor.sql +1 -1
- package/templates/minimal-app/apps/server/drizzle/0006_lazy_blue_marvel.sql +158 -158
- package/templates/minimal-app/apps/server/drizzle/0007_abnormal_ultimo.sql +1 -1
- package/templates/minimal-app/apps/server/drizzle/0008_steady_nekra.sql +86 -86
- package/templates/minimal-app/apps/server/drizzle/0009_empty_starjammers.sql +45 -0
- package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +2624 -2624
- package/templates/minimal-app/apps/server/drizzle/meta/0001_snapshot.json.tpl +2670 -2670
- package/templates/minimal-app/apps/server/drizzle/meta/0002_snapshot.json.tpl +2715 -2715
- package/templates/minimal-app/apps/server/drizzle/meta/0003_snapshot.json.tpl +2687 -2687
- package/templates/minimal-app/apps/server/drizzle/meta/0004_snapshot.json.tpl +2497 -2497
- package/templates/minimal-app/apps/server/drizzle/meta/0005_snapshot.json.tpl +2483 -2483
- package/templates/minimal-app/apps/server/drizzle/meta/0006_snapshot.json.tpl +3602 -3602
- package/templates/minimal-app/apps/server/drizzle/meta/0007_snapshot.json.tpl +3619 -3619
- package/templates/minimal-app/apps/server/drizzle/meta/0008_snapshot.json.tpl +3619 -3619
- package/templates/minimal-app/apps/server/drizzle/meta/0009_snapshot.json.tpl +3687 -0
- package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +8 -1
- package/templates/minimal-app/apps/server/package.json.tpl +57 -57
- package/templates/minimal-app/apps/shared/package.json.tpl +27 -27
- package/templates/minimal-app/apps/webapp/AGENTS.md.tpl +24 -24
- package/templates/minimal-app/apps/webapp/index.html.tpl +22 -22
- package/templates/minimal-app/apps/webapp/package.json.tpl +53 -53
- package/templates/minimal-app/apps/webapp/public/logo.svg.tpl +6 -6
- package/templates/minimal-app/pnpm-workspace.yaml.tpl +15 -15
- package/templates/minimal-app/apps/expo/expo-env.d.ts.tpl +0 -3
- package/templates/minimal-app/apps/expo/uniwind-types.d.ts.tpl +0 -10
|
@@ -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.
|