wcz-layout 7.6.0 → 7.6.2

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.

Potentially problematic release.


This version of wcz-layout might be problematic. Click here for more details.

Files changed (34) hide show
  1. package/dist/{RouterListItemButton-CvfZk2zD.js → RouterListItemButton-DeaQB4ym.js} +1 -1
  2. package/dist/{RouterListItemButton-CvfZk2zD.js.map → RouterListItemButton-DeaQB4ym.js.map} +1 -1
  3. package/dist/components/core/Layout.d.ts +1 -1
  4. package/dist/components/core/navigation/NavigationList.d.ts +4 -4
  5. package/dist/components/core/navigation/NavigationListItem.d.ts +3 -3
  6. package/dist/components.js +2 -2
  7. package/dist/hooks.js +1 -1
  8. package/dist/index.js +617 -621
  9. package/dist/index.js.map +1 -1
  10. package/dist/lib/auth/msalClient.d.ts +8 -2
  11. package/dist/middleware.js +11 -11
  12. package/dist/models/Navigation.d.ts +23 -11
  13. package/dist/{queries-DzKY6YXz.js → queries-D-DV5lCw.js} +3 -3
  14. package/dist/{queries-DzKY6YXz.js.map → queries-D-DV5lCw.js.map} +1 -1
  15. package/dist/query.js +2 -2
  16. package/dist/{queryClient-uWNhcABg.js → queryClient-B__OEZ70.js} +1 -1
  17. package/dist/{queryClient-uWNhcABg.js.map → queryClient-B__OEZ70.js.map} +1 -1
  18. package/dist/{msalClient-BLrbVP5z.js → utils-C4oJ0tr5.js} +58 -47
  19. package/dist/utils-C4oJ0tr5.js.map +1 -0
  20. package/dist/utils.js +5 -5
  21. package/package.json +38 -6
  22. package/skills/api-routes/SKILL.md +251 -0
  23. package/skills/auth/SKILL.md +268 -0
  24. package/skills/data-grid/SKILL.md +229 -0
  25. package/skills/database-schema/SKILL.md +182 -0
  26. package/skills/dialogs-notifications/SKILL.md +241 -0
  27. package/skills/forms-validation/SKILL.md +331 -0
  28. package/skills/forms-validation/references/field-components.md +212 -0
  29. package/skills/layout-navigation/SKILL.md +259 -0
  30. package/skills/project-initialization/SKILL.md +181 -0
  31. package/skills/project-structure/SKILL.md +157 -0
  32. package/skills/tanstack-db-collections/SKILL.md +270 -0
  33. package/skills/ui-pages/SKILL.md +278 -0
  34. package/dist/msalClient-BLrbVP5z.js.map +0 -1
@@ -0,0 +1,212 @@
1
+ # Field Components Reference
2
+
3
+ All field components are accessed via `field.*` inside a `form.AppField` children render prop. Props listed below are the MUI props you can pass — `FormOmittedProps` (`name`, `value`, `onChange`, `onBlur`, `error`, `helperText`, `renderInput`, `type`, `aria-label`) are automatically stripped and managed by TanStack Form.
4
+
5
+ ## TextField
6
+
7
+ ```typescript
8
+ <form.AppField
9
+ name="title"
10
+ children={(field) => (
11
+ <field.TextField
12
+ label="Title"
13
+ required
14
+ multiline // optional: enables textarea
15
+ rows={3} // optional: fixed row count for multiline
16
+ placeholder="..." // optional
17
+ disabled // optional
18
+ slotProps={{ // optional: MUI slot props
19
+ input: { startAdornment: <InputAdornment position="start">$</InputAdornment> }
20
+ }}
21
+ />
22
+ )}
23
+ />
24
+ ```
25
+
26
+ ## NumberField
27
+
28
+ ```typescript
29
+ <form.AppField
30
+ name="quantity"
31
+ children={(field) => (
32
+ <field.NumberField
33
+ label="Quantity"
34
+ required
35
+ slotProps={{
36
+ input: { inputProps: { min: 0, max: 100, step: 1 } }
37
+ }}
38
+ />
39
+ )}
40
+ />
41
+ ```
42
+
43
+ ## Autocomplete
44
+
45
+ ```typescript
46
+ <form.AppField
47
+ name="assignee"
48
+ children={(field) => (
49
+ <field.Autocomplete
50
+ label="Assignee"
51
+ options={users}
52
+ getOptionLabel={(user) => user.displayName}
53
+ isOptionEqualToValue={(option, value) => option.id === value.id}
54
+ multiple // optional: multi-select
55
+ freeSolo // optional: allow custom input
56
+ loading={isLoading} // optional: show loading indicator
57
+ />
58
+ )}
59
+ />
60
+ ```
61
+
62
+ ## Checkbox
63
+
64
+ ```typescript
65
+ <form.AppField
66
+ name="isActive"
67
+ children={(field) => (
68
+ <field.Checkbox
69
+ label="Active"
70
+ disabled // optional
71
+ />
72
+ )}
73
+ />
74
+ ```
75
+
76
+ Renders `FormControlLabel` wrapping MUI `Checkbox`.
77
+
78
+ ## Switch
79
+
80
+ ```typescript
81
+ <form.AppField
82
+ name="notifications"
83
+ children={(field) => (
84
+ <field.Switch
85
+ label="Enable Notifications"
86
+ disabled // optional
87
+ />
88
+ )}
89
+ />
90
+ ```
91
+
92
+ Renders `FormControlLabel` wrapping MUI `Switch`.
93
+
94
+ ## RadioGroup
95
+
96
+ ```typescript
97
+ <form.AppField
98
+ name="priority"
99
+ children={(field) => (
100
+ <field.RadioGroup
101
+ label="Priority"
102
+ options={[
103
+ { label: "Low", value: "low" },
104
+ { label: "Medium", value: "medium" },
105
+ { label: "High", value: "high" },
106
+ ]}
107
+ row // optional: horizontal layout
108
+ />
109
+ )}
110
+ />
111
+ ```
112
+
113
+ **Warning:** `onChange` returns `event.target.value` as a string. Use string values.
114
+
115
+ ## Slider
116
+
117
+ ```typescript
118
+ <form.AppField
119
+ name="rating"
120
+ children={(field) => (
121
+ <field.Slider
122
+ min={0}
123
+ max={10}
124
+ step={1}
125
+ marks
126
+ valueLabelDisplay="auto"
127
+ />
128
+ )}
129
+ />
130
+ ```
131
+
132
+ ## DatePicker
133
+
134
+ ```typescript
135
+ <form.AppField
136
+ name="dueDate"
137
+ children={(field) => (
138
+ <field.DatePicker
139
+ label="Due Date"
140
+ minDate={dayjs()} // optional
141
+ maxDate={dayjs().add(1, "year")} // optional
142
+ />
143
+ )}
144
+ />
145
+ ```
146
+
147
+ ## DateRangePicker
148
+
149
+ ```typescript
150
+ <form.AppField
151
+ name="dateRange"
152
+ children={(field) => (
153
+ <field.DateRangePicker
154
+ localeText={{ start: "Start", end: "End" }}
155
+ />
156
+ )}
157
+ />
158
+ ```
159
+
160
+ ## TimePicker
161
+
162
+ ```typescript
163
+ <form.AppField
164
+ name="startTime"
165
+ children={(field) => (
166
+ <field.TimePicker
167
+ label="Start Time"
168
+ ampm={false} // optional: 24-hour format
169
+ />
170
+ )}
171
+ />
172
+ ```
173
+
174
+ ## TimeRangePicker
175
+
176
+ ```typescript
177
+ <form.AppField
178
+ name="timeRange"
179
+ children={(field) => (
180
+ <field.TimeRangePicker
181
+ localeText={{ start: "From", end: "To" }}
182
+ />
183
+ )}
184
+ />
185
+ ```
186
+
187
+ ## DateTimePicker
188
+
189
+ ```typescript
190
+ <form.AppField
191
+ name="scheduledAt"
192
+ children={(field) => (
193
+ <field.DateTimePicker
194
+ label="Scheduled At"
195
+ ampm={false}
196
+ />
197
+ )}
198
+ />
199
+ ```
200
+
201
+ ## DateTimeRangePicker
202
+
203
+ ```typescript
204
+ <form.AppField
205
+ name="availability"
206
+ children={(field) => (
207
+ <field.DateTimeRangePicker
208
+ localeText={{ start: "From", end: "Until" }}
209
+ />
210
+ )}
211
+ />
212
+ ```
@@ -0,0 +1,259 @@
1
+ ---
2
+ name: layout-navigation
3
+ description: >
4
+ Configure LayoutProvider with theme, options, and Navigation structure.
5
+ Shell: AppBar with env chip + NavigationRail (permanent sm+, modal xs).
6
+ Navigation items: page (title, icon, to/href), header, divider.
7
+ LayoutOptions: showShell, snackbarOrigin, publicRoutes. Dark mode via
8
+ theme.applyStyles("dark", ...) with colorSchemeSelector
9
+ data-mui-color-scheme. i18n with i18next, locale files auto-detected.
10
+ Activate when configuring the layout shell, navigation, or theming.
11
+ type: core
12
+ library: wcz-layout
13
+ library_version: "7.6.1"
14
+ sources:
15
+ - "wcz-layout:src/providers/LayoutProvider.tsx"
16
+ - "wcz-layout:src/components/core/Layout.tsx"
17
+ - "wcz-layout:src/models/Navigation.ts"
18
+ - "wcz-layout:src/models/LayoutOptions.ts"
19
+ - "wcz-layout:src/lib/theme.ts"
20
+ ---
21
+
22
+ # Layout & Navigation
23
+
24
+ ## Setup
25
+
26
+ The layout shell is configured in the root route via `LayoutProvider`:
27
+
28
+ ```typescript
29
+ // src/routes/__root.tsx
30
+ import { LayoutProvider, rootRouteHead, RouterNotFound, RouterError } from "wcz-layout";
31
+ import { theme } from "~/lib/theme";
32
+ import { navigation } from "~/navigation";
33
+ import type { LayoutOptions } from "wcz-layout/models";
34
+
35
+ const options: LayoutOptions = {
36
+ showShell: true,
37
+ publicRoutes: ["/login"],
38
+ };
39
+
40
+ export const Route = createRootRouteWithContext()({
41
+ head: rootRouteHead,
42
+ component: RootComponent,
43
+ notFoundComponent: RouterNotFound,
44
+ errorComponent: RouterError,
45
+ });
46
+
47
+ function RootComponent() {
48
+ return (
49
+ <LayoutProvider theme={theme} navigation={navigation} options={options}>
50
+ <Outlet />
51
+ </LayoutProvider>
52
+ );
53
+ }
54
+ ```
55
+
56
+ ## Core Patterns
57
+
58
+ ### Navigation structure
59
+
60
+ Navigation is an array of items with three types — discriminated by the presence of `kind`:
61
+
62
+ ```typescript
63
+ import type { Navigation } from "wcz-layout/models";
64
+ import HomeIcon from "@mui/icons-material/Home";
65
+ import ListIcon from "@mui/icons-material/List";
66
+ import SettingsIcon from "@mui/icons-material/Settings";
67
+
68
+ export const navigation: Navigation = [
69
+ { title: "Home", icon: <HomeIcon />, to: "/" },
70
+ { kind: "divider" },
71
+ { kind: "header", title: "Management" },
72
+ { title: "Todos", icon: <ListIcon />, to: "/todos" },
73
+ { kind: "divider" },
74
+ { title: "Settings", icon: <SettingsIcon />, to: "/settings" },
75
+ ];
76
+ ```
77
+
78
+ ### Navigation item types
79
+
80
+ | Type | Shape | Required fields |
81
+ | --------- | --------------------------- | --------------- |
82
+ | Page item | `{ title, icon, to }` | `title`, `icon` |
83
+ | Divider | `{ kind: "divider" }` | `kind` |
84
+ | Header | `{ kind: "header", title }` | `kind`, `title` |
85
+
86
+ Page items also accept:
87
+
88
+ - `to` — internal route (TanStack Router link, type-safe)
89
+ - `href` — external URL (opens in new tab)
90
+ - `params`, `search` — TanStack Router link params
91
+ - `children` — nested navigation items (sub-menu)
92
+ - `hidden` — conditionally hide the item
93
+
94
+ ### Nested navigation
95
+
96
+ ```typescript
97
+ const navigation: Navigation = [
98
+ {
99
+ title: "Admin",
100
+ icon: <AdminIcon />,
101
+ to: "/admin",
102
+ children: [
103
+ { title: "Users", icon: <PeopleIcon />, to: "/admin/users" },
104
+ { title: "Roles", icon: <SecurityIcon />, to: "/admin/roles" },
105
+ ],
106
+ },
107
+ ];
108
+ ```
109
+
110
+ ### LayoutOptions
111
+
112
+ ```typescript
113
+ interface LayoutOptions {
114
+ showShell?: boolean; // Show AppBar + NavigationRail (default: true)
115
+ snackbarOrigin?: SnackbarOrigin; // Notification position
116
+ publicRoutes?: string[]; // Routes that skip authentication
117
+ }
118
+ ```
119
+
120
+ ### Theme configuration
121
+
122
+ The theme is pre-configured in the template at `src/lib/theme.ts`. Use MUI's `extendTheme` to customize:
123
+
124
+ ```typescript
125
+ // src/lib/theme.ts
126
+ import { extendTheme } from "@mui/material/styles";
127
+
128
+ export const theme = extendTheme({
129
+ colorSchemes: {
130
+ light: { palette: { primary: { main: "#1976d2" } } },
131
+ dark: { palette: { primary: { main: "#90caf9" } } },
132
+ },
133
+ colorSchemeSelector: "data-mui-color-scheme",
134
+ });
135
+ ```
136
+
137
+ ### Dark mode styling
138
+
139
+ Always use `theme.applyStyles("dark", ...)` for mode-specific styling:
140
+
141
+ ```typescript
142
+ <Box
143
+ sx={(theme) => ({
144
+ backgroundColor: "#fff",
145
+ ...theme.applyStyles("dark", {
146
+ backgroundColor: "#121212",
147
+ }),
148
+ })}
149
+ />
150
+ ```
151
+
152
+ ### i18n setup
153
+
154
+ Locale files in `src/locales/` are auto-detected by `viteWczLayout()`:
155
+
156
+ ```
157
+ src/locales/en.json # English (default)
158
+ src/locales/cs.json # Czech
159
+ ```
160
+
161
+ The Vite plugin generates the `virtual:wcz-layout` module with all locale resources. `LayoutProvider` initializes `i18next` with language detection (cookie-based, 1-year expiry) and syncs Zod and DayJS locales on language change.
162
+
163
+ ```typescript
164
+ import { useTranslation } from "react-i18next";
165
+
166
+ function MyComponent() {
167
+ const { t } = useTranslation();
168
+ return <Typography>{t("welcome")}</Typography>;
169
+ }
170
+ ```
171
+
172
+ ### Root route head
173
+
174
+ `rootRouteHead` provides meta tags and manifest link:
175
+
176
+ ```typescript
177
+ export const Route = createRootRouteWithContext()({
178
+ head: rootRouteHead,
179
+ // optionally: head: rootRouteHead({ manifest: "/manifest.json" }),
180
+ });
181
+ ```
182
+
183
+ ### Service worker
184
+
185
+ `LayoutProvider` registers `/sw.js` on mount for PWA support.
186
+
187
+ ## Common Mistakes
188
+
189
+ ### HIGH Using theme.palette for dark mode instead of theme.applyStyles
190
+
191
+ Wrong:
192
+
193
+ ```typescript
194
+ sx={{ color: mode === "dark" ? "white" : "black" }}
195
+ ```
196
+
197
+ Correct:
198
+
199
+ ```typescript
200
+ sx={(theme) => ({
201
+ color: "black",
202
+ ...theme.applyStyles("dark", { color: "white" }),
203
+ })}
204
+ ```
205
+
206
+ The app uses `colorSchemeSelector: "data-mui-color-scheme"`. Mode-specific styles must use `theme.applyStyles("dark", ...)`, not conditional palette checks or mode variables.
207
+
208
+ Source: copilot-instructions.md / wcz-layout:src/lib/theme.ts
209
+
210
+ Cross-skill: See also skills/ui-pages/SKILL.md § Common Mistakes
211
+
212
+ ### HIGH Using href for internal routes in Navigation
213
+
214
+ Wrong:
215
+
216
+ ```typescript
217
+ { title: "Todos", icon: <ListIcon />, href: "/todos" }
218
+ ```
219
+
220
+ Correct:
221
+
222
+ ```typescript
223
+ { title: "Todos", icon: <ListIcon />, to: "/todos" }
224
+ ```
225
+
226
+ `NavigationPageItem` supports dual destination: `to` for internal TanStack Router links, `href` for external URLs. Using `href` for internal routes causes full page reloads.
227
+
228
+ Source: wcz-layout:src/models/Navigation.ts
229
+
230
+ ### MEDIUM Creating NavigationPageItem without icon
231
+
232
+ Wrong:
233
+
234
+ ```typescript
235
+ { title: "Todos", to: "/todos" }
236
+ ```
237
+
238
+ Correct:
239
+
240
+ ```typescript
241
+ { title: "Todos", icon: <ListIcon />, to: "/todos" }
242
+ ```
243
+
244
+ `NavigationPageItem` requires both `title` and `icon`. Missing `icon` renders an empty slot in the NavigationRail.
245
+
246
+ Source: wcz-layout:src/models/Navigation.ts
247
+
248
+ ### MEDIUM Adding locale without updating supportedLngs
249
+
250
+ This is actually **not** an issue — the Vite plugin auto-detects locale files in `src/locales/`. Adding a new JSON file (e.g. `de.json`) is sufficient; `supportedLngs` is derived from `Object.keys(resources)` automatically.
251
+
252
+ Source: wcz-layout:src/providers/LayoutProvider.tsx
253
+
254
+ ---
255
+
256
+ See also:
257
+
258
+ - skills/ui-pages/SKILL.md — Pages render inside the layout shell.
259
+ - skills/auth/SKILL.md — publicRoutes bypass auth; permission guards need auth context.
@@ -0,0 +1,181 @@
1
+ ---
2
+ name: project-initialization
3
+ description: >
4
+ Scaffold a new wcz-layout project from the template. Covers placeholder
5
+ replacement across three naming conventions (kebab-case, Title Case,
6
+ snake_case), .env and .env.local configuration, VITE_ENTRA_CLIENT_ID,
7
+ VITE_APP_TITLE, VITE_MUI_LICENSE_KEY, favicon generation via favicon.io,
8
+ and viteWczLayout() Vite plugin registration. Activate when starting a
9
+ new internal project or configuring environment variables.
10
+ type: lifecycle
11
+ library: wcz-layout
12
+ library_version: "7.6.1"
13
+ sources:
14
+ - "wcz-layout:src/env.ts"
15
+ - "wcz-layout:src/lib/vite-plugin.ts"
16
+ - "wcz-layout:vite.config.ts"
17
+ - "wcz-layout:.env"
18
+ ---
19
+
20
+ # Project Initialization
21
+
22
+ ## Setup
23
+
24
+ After cloning the template repository, initialize the project in this order:
25
+
26
+ ### 1. Replace all template placeholders
27
+
28
+ Use your editor's replace-all (across all files) for each naming convention:
29
+
30
+ | Find | Replace with | Used in |
31
+ | -------------- | ------------ | ------------------------------------------------ |
32
+ | `project-name` | `app-store` | package.json name, URLs, CSS classes |
33
+ | `Project Name` | `App Store` | VITE_APP_TITLE, display strings, docs |
34
+ | `my_app` | `app_store` | Database names, env vars, snake_case identifiers |
35
+
36
+ ### 2. Configure environment variables
37
+
38
+ ```sh
39
+ # .env — committed to repo, shared across team
40
+ VITE_ENTRA_CLIENT_ID=your-entra-client-id
41
+ VITE_ENTRA_TENANT_ID=your-tenant-id
42
+ VITE_APP_TITLE=App Store
43
+ VITE_MUI_LICENSE_KEY=your-mui-license-key
44
+ ```
45
+
46
+ Create `.env.local` (gitignored) for local secret overrides:
47
+
48
+ ```sh
49
+ # .env.local — never committed
50
+ VITE_ENTRA_CLIENT_ID=your-dev-client-id
51
+ ENTRA_CLIENT_ID=your-server-client-id
52
+ ENTRA_TENANT_ID=your-server-tenant-id
53
+ ENTRA_CLIENT_SECRET=your-client-secret
54
+ ```
55
+
56
+ ### 3. Generate favicon
57
+
58
+ Go to https://favicon.io/favicon-converter/, upload your logo, and place the generated files in `public/`.
59
+
60
+ ### 4. Verify Vite plugin registration
61
+
62
+ ```typescript
63
+ // vite.config.ts
64
+ import { viteWczLayout } from "wcz-layout/vite";
65
+
66
+ export default defineConfig({
67
+ plugins: [
68
+ tanstackStart(),
69
+ nitro(),
70
+ viteReact(),
71
+ babel(reactCompilerPreset()),
72
+ checker({ typescript: true }),
73
+ viteWczLayout(),
74
+ ],
75
+ });
76
+ ```
77
+
78
+ ## Core Patterns
79
+
80
+ ### Environment validation with createEnv
81
+
82
+ ```typescript
83
+ // src/env.ts
84
+ import { createEnv } from "wcz-layout/utils";
85
+ import { z } from "zod";
86
+
87
+ export const clientEnv = createEnv({
88
+ clientPrefix: "VITE_",
89
+ client: {
90
+ VITE_ENTRA_CLIENT_ID: z.string(),
91
+ VITE_ENTRA_TENANT_ID: z.string(),
92
+ VITE_APP_TITLE: z.string(),
93
+ VITE_MUI_LICENSE_KEY: z.string(),
94
+ },
95
+ runtimeEnv: import.meta.env,
96
+ emptyStringAsUndefined: true,
97
+ });
98
+
99
+ export const serverEnv = createEnv({
100
+ server: {
101
+ ENTRA_CLIENT_ID: z.string(),
102
+ ENTRA_TENANT_ID: z.string(),
103
+ ENTRA_CLIENT_SECRET: z.string(),
104
+ },
105
+ runtimeEnv: process.env,
106
+ emptyStringAsUndefined: true,
107
+ });
108
+ ```
109
+
110
+ ### Vault secrets for local development
111
+
112
+ The `viteWczLayout()` plugin automatically loads secrets from HashiCorp Vault during `vite dev` if `VAULT_ADDRESS`, `VAULT_USERNAME`, `VAULT_PASSWORD`, and `VAULT_SECRET_PATH` are set in `.env`. Vault-loaded secrets only populate `process.env` keys that are not already set.
113
+
114
+ ## Common Mistakes
115
+
116
+ ### CRITICAL Missing viteWczLayout() in Vite config
117
+
118
+ Wrong:
119
+
120
+ ```typescript
121
+ // vite.config.ts
122
+ export default defineConfig({
123
+ plugins: [tanstackStart(), viteReact()],
124
+ });
125
+ ```
126
+
127
+ Correct:
128
+
129
+ ```typescript
130
+ // vite.config.ts
131
+ import { viteWczLayout } from "wcz-layout/vite";
132
+ export default defineConfig({
133
+ plugins: [tanstackStart(), viteReact(), viteWczLayout()],
134
+ });
135
+ ```
136
+
137
+ Without `viteWczLayout()`, the `virtual:wcz-layout` module won't resolve, breaking i18n resources, permissions, and scopes at build time.
138
+
139
+ Source: wcz-layout:src/lib/vite-plugin.ts
140
+
141
+ ### HIGH Forgetting .env.local for local secrets
142
+
143
+ `.env` is committed to the repo and shared across the team. Local overrides (Entra client IDs, secrets) must go in `.env.local` which is gitignored. Committing secrets to `.env` exposes them in the repository.
144
+
145
+ Source: maintainer interview
146
+
147
+ ### HIGH Not replacing all naming conventions
148
+
149
+ The template uses three naming conventions. Use replace-all across the entire project for each one. Missing any convention leaves stale references in package.json, env files, or source imports.
150
+
151
+ Source: maintainer interview
152
+
153
+ ### MEDIUM Empty string env vars pass silently
154
+
155
+ Wrong:
156
+
157
+ ```sh
158
+ # .env
159
+ VITE_APP_TITLE=
160
+ ```
161
+
162
+ Correct:
163
+
164
+ ```sh
165
+ # .env
166
+ VITE_APP_TITLE=My Application
167
+ ```
168
+
169
+ `createEnv` uses `emptyStringAsUndefined: true`, so empty strings become `undefined` and fail Zod validation at runtime, not build time.
170
+
171
+ Source: wcz-layout:src/env.ts
172
+
173
+ ### HIGH Tension: Simplicity vs. full-stack rigor
174
+
175
+ This skill's simple setup conflicts with production readiness from api-routes. Agents scaffolding a quick prototype may skip authorizationMiddleware and validationMiddleware, producing code that works locally but is insecure in production. Always include middleware from the start.
176
+
177
+ See also: skills/api-routes/SKILL.md § Common Mistakes
178
+
179
+ ---
180
+
181
+ See also: skills/project-structure/SKILL.md — After init, understand where to place new code.