autoworkflow 3.1.4 → 3.5.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/.claude/commands/analyze.md +19 -0
- package/.claude/commands/audit.md +174 -11
- package/.claude/commands/build.md +39 -0
- package/.claude/commands/commit.md +25 -0
- package/.claude/commands/fix.md +23 -0
- package/.claude/commands/plan.md +18 -0
- package/.claude/commands/suggest.md +23 -0
- package/.claude/commands/verify.md +18 -0
- package/.claude/hooks/post-bash-router.sh +20 -0
- package/.claude/hooks/post-commit.sh +140 -0
- package/.claude/hooks/pre-edit.sh +129 -0
- package/.claude/hooks/session-check.sh +79 -0
- package/.claude/settings.json +40 -6
- package/.claude/settings.local.json +3 -1
- package/.claude/skills/actix.md +337 -0
- package/.claude/skills/alembic.md +504 -0
- package/.claude/skills/angular.md +237 -0
- package/.claude/skills/api-design.md +187 -0
- package/.claude/skills/aspnet-core.md +377 -0
- package/.claude/skills/astro.md +245 -0
- package/.claude/skills/auth-clerk.md +327 -0
- package/.claude/skills/auth-firebase.md +367 -0
- package/.claude/skills/auth-nextauth.md +359 -0
- package/.claude/skills/auth-supabase.md +368 -0
- package/.claude/skills/axum.md +386 -0
- package/.claude/skills/blazor.md +456 -0
- package/.claude/skills/chi.md +348 -0
- package/.claude/skills/code-review.md +133 -0
- package/.claude/skills/csharp.md +296 -0
- package/.claude/skills/css-modules.md +325 -0
- package/.claude/skills/cypress.md +343 -0
- package/.claude/skills/debugging.md +133 -0
- package/.claude/skills/diesel.md +392 -0
- package/.claude/skills/django.md +301 -0
- package/.claude/skills/docker.md +319 -0
- package/.claude/skills/doctrine.md +473 -0
- package/.claude/skills/documentation.md +182 -0
- package/.claude/skills/dotnet.md +409 -0
- package/.claude/skills/drizzle.md +293 -0
- package/.claude/skills/echo.md +321 -0
- package/.claude/skills/eloquent.md +256 -0
- package/.claude/skills/emotion.md +426 -0
- package/.claude/skills/entity-framework.md +370 -0
- package/.claude/skills/express.md +316 -0
- package/.claude/skills/fastapi.md +329 -0
- package/.claude/skills/fastify.md +299 -0
- package/.claude/skills/fiber.md +315 -0
- package/.claude/skills/flask.md +322 -0
- package/.claude/skills/gin.md +342 -0
- package/.claude/skills/git.md +116 -0
- package/.claude/skills/github-actions.md +353 -0
- package/.claude/skills/go.md +377 -0
- package/.claude/skills/gorm.md +409 -0
- package/.claude/skills/graphql.md +478 -0
- package/.claude/skills/hibernate.md +379 -0
- package/.claude/skills/hono.md +306 -0
- package/.claude/skills/java.md +400 -0
- package/.claude/skills/jest.md +313 -0
- package/.claude/skills/jpa.md +282 -0
- package/.claude/skills/kotlin.md +347 -0
- package/.claude/skills/kubernetes.md +363 -0
- package/.claude/skills/laravel.md +414 -0
- package/.claude/skills/mcp-browser.md +320 -0
- package/.claude/skills/mcp-database.md +219 -0
- package/.claude/skills/mcp-fetch.md +241 -0
- package/.claude/skills/mcp-filesystem.md +204 -0
- package/.claude/skills/mcp-github.md +217 -0
- package/.claude/skills/mcp-memory.md +240 -0
- package/.claude/skills/mcp-search.md +218 -0
- package/.claude/skills/mcp-slack.md +262 -0
- package/.claude/skills/micronaut.md +388 -0
- package/.claude/skills/mongodb.md +319 -0
- package/.claude/skills/mongoose.md +355 -0
- package/.claude/skills/mysql.md +281 -0
- package/.claude/skills/nestjs.md +335 -0
- package/.claude/skills/nextjs-app-router.md +260 -0
- package/.claude/skills/nextjs-pages.md +172 -0
- package/.claude/skills/nuxt.md +202 -0
- package/.claude/skills/openapi.md +489 -0
- package/.claude/skills/performance.md +199 -0
- package/.claude/skills/php.md +398 -0
- package/.claude/skills/playwright.md +371 -0
- package/.claude/skills/postgresql.md +257 -0
- package/.claude/skills/prisma.md +293 -0
- package/.claude/skills/pydantic.md +304 -0
- package/.claude/skills/pytest.md +313 -0
- package/.claude/skills/python.md +272 -0
- package/.claude/skills/quarkus.md +377 -0
- package/.claude/skills/react.md +230 -0
- package/.claude/skills/redis.md +391 -0
- package/.claude/skills/refactoring.md +143 -0
- package/.claude/skills/remix.md +246 -0
- package/.claude/skills/rest-api.md +490 -0
- package/.claude/skills/rocket.md +366 -0
- package/.claude/skills/rust.md +341 -0
- package/.claude/skills/sass.md +380 -0
- package/.claude/skills/sea-orm.md +382 -0
- package/.claude/skills/security.md +167 -0
- package/.claude/skills/sequelize.md +395 -0
- package/.claude/skills/spring-boot.md +416 -0
- package/.claude/skills/sqlalchemy.md +269 -0
- package/.claude/skills/sqlx-rust.md +408 -0
- package/.claude/skills/state-jotai.md +346 -0
- package/.claude/skills/state-mobx.md +353 -0
- package/.claude/skills/state-pinia.md +431 -0
- package/.claude/skills/state-redux.md +337 -0
- package/.claude/skills/state-tanstack-query.md +434 -0
- package/.claude/skills/state-zustand.md +340 -0
- package/.claude/skills/styled-components.md +403 -0
- package/.claude/skills/svelte.md +238 -0
- package/.claude/skills/sveltekit.md +207 -0
- package/.claude/skills/symfony.md +437 -0
- package/.claude/skills/tailwind.md +279 -0
- package/.claude/skills/terraform.md +394 -0
- package/.claude/skills/testing-library.md +371 -0
- package/.claude/skills/trpc.md +426 -0
- package/.claude/skills/typeorm.md +368 -0
- package/.claude/skills/vitest.md +330 -0
- package/.claude/skills/vue.md +202 -0
- package/.claude/skills/warp.md +365 -0
- package/README.md +135 -52
- package/package.json +1 -1
- package/system/triggers.md +152 -11
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
# OpenAPI Skill
|
|
2
|
+
|
|
3
|
+
## Complete Specification
|
|
4
|
+
\`\`\`yaml
|
|
5
|
+
openapi: 3.1.0
|
|
6
|
+
info:
|
|
7
|
+
title: My API
|
|
8
|
+
description: RESTful API for user management
|
|
9
|
+
version: 1.0.0
|
|
10
|
+
contact:
|
|
11
|
+
name: API Support
|
|
12
|
+
email: support@example.com
|
|
13
|
+
license:
|
|
14
|
+
name: MIT
|
|
15
|
+
url: https://opensource.org/licenses/MIT
|
|
16
|
+
|
|
17
|
+
servers:
|
|
18
|
+
- url: https://api.example.com/v1
|
|
19
|
+
description: Production
|
|
20
|
+
- url: https://staging-api.example.com/v1
|
|
21
|
+
description: Staging
|
|
22
|
+
- url: http://localhost:3000/v1
|
|
23
|
+
description: Development
|
|
24
|
+
|
|
25
|
+
tags:
|
|
26
|
+
- name: Users
|
|
27
|
+
description: User management operations
|
|
28
|
+
- name: Posts
|
|
29
|
+
description: Blog post operations
|
|
30
|
+
- name: Auth
|
|
31
|
+
description: Authentication endpoints
|
|
32
|
+
|
|
33
|
+
paths:
|
|
34
|
+
/users:
|
|
35
|
+
get:
|
|
36
|
+
tags: [Users]
|
|
37
|
+
summary: List users
|
|
38
|
+
description: Returns a paginated list of users
|
|
39
|
+
operationId: listUsers
|
|
40
|
+
parameters:
|
|
41
|
+
- $ref: '#/components/parameters/PageParam'
|
|
42
|
+
- $ref: '#/components/parameters/LimitParam'
|
|
43
|
+
- name: status
|
|
44
|
+
in: query
|
|
45
|
+
schema:
|
|
46
|
+
type: string
|
|
47
|
+
enum: [active, inactive, pending]
|
|
48
|
+
- name: search
|
|
49
|
+
in: query
|
|
50
|
+
schema:
|
|
51
|
+
type: string
|
|
52
|
+
description: Search by name or email
|
|
53
|
+
responses:
|
|
54
|
+
'200':
|
|
55
|
+
description: Successful response
|
|
56
|
+
content:
|
|
57
|
+
application/json:
|
|
58
|
+
schema:
|
|
59
|
+
$ref: '#/components/schemas/UserListResponse'
|
|
60
|
+
'401':
|
|
61
|
+
$ref: '#/components/responses/Unauthorized'
|
|
62
|
+
security:
|
|
63
|
+
- BearerAuth: []
|
|
64
|
+
|
|
65
|
+
post:
|
|
66
|
+
tags: [Users]
|
|
67
|
+
summary: Create user
|
|
68
|
+
operationId: createUser
|
|
69
|
+
requestBody:
|
|
70
|
+
required: true
|
|
71
|
+
content:
|
|
72
|
+
application/json:
|
|
73
|
+
schema:
|
|
74
|
+
$ref: '#/components/schemas/CreateUserInput'
|
|
75
|
+
examples:
|
|
76
|
+
basic:
|
|
77
|
+
summary: Basic user
|
|
78
|
+
value:
|
|
79
|
+
email: user@example.com
|
|
80
|
+
name: John Doe
|
|
81
|
+
password: securepassword123
|
|
82
|
+
responses:
|
|
83
|
+
'201':
|
|
84
|
+
description: User created
|
|
85
|
+
content:
|
|
86
|
+
application/json:
|
|
87
|
+
schema:
|
|
88
|
+
$ref: '#/components/schemas/UserResponse'
|
|
89
|
+
headers:
|
|
90
|
+
Location:
|
|
91
|
+
schema:
|
|
92
|
+
type: string
|
|
93
|
+
description: URL of created resource
|
|
94
|
+
'400':
|
|
95
|
+
$ref: '#/components/responses/ValidationError'
|
|
96
|
+
'409':
|
|
97
|
+
description: Email already exists
|
|
98
|
+
content:
|
|
99
|
+
application/json:
|
|
100
|
+
schema:
|
|
101
|
+
$ref: '#/components/schemas/Error'
|
|
102
|
+
|
|
103
|
+
/users/{id}:
|
|
104
|
+
get:
|
|
105
|
+
tags: [Users]
|
|
106
|
+
summary: Get user by ID
|
|
107
|
+
operationId: getUserById
|
|
108
|
+
parameters:
|
|
109
|
+
- $ref: '#/components/parameters/UserIdParam'
|
|
110
|
+
responses:
|
|
111
|
+
'200':
|
|
112
|
+
description: User found
|
|
113
|
+
content:
|
|
114
|
+
application/json:
|
|
115
|
+
schema:
|
|
116
|
+
$ref: '#/components/schemas/UserResponse'
|
|
117
|
+
'404':
|
|
118
|
+
$ref: '#/components/responses/NotFound'
|
|
119
|
+
|
|
120
|
+
patch:
|
|
121
|
+
tags: [Users]
|
|
122
|
+
summary: Update user
|
|
123
|
+
operationId: updateUser
|
|
124
|
+
parameters:
|
|
125
|
+
- $ref: '#/components/parameters/UserIdParam'
|
|
126
|
+
requestBody:
|
|
127
|
+
required: true
|
|
128
|
+
content:
|
|
129
|
+
application/json:
|
|
130
|
+
schema:
|
|
131
|
+
$ref: '#/components/schemas/UpdateUserInput'
|
|
132
|
+
responses:
|
|
133
|
+
'200':
|
|
134
|
+
description: User updated
|
|
135
|
+
content:
|
|
136
|
+
application/json:
|
|
137
|
+
schema:
|
|
138
|
+
$ref: '#/components/schemas/UserResponse'
|
|
139
|
+
'404':
|
|
140
|
+
$ref: '#/components/responses/NotFound'
|
|
141
|
+
security:
|
|
142
|
+
- BearerAuth: []
|
|
143
|
+
|
|
144
|
+
delete:
|
|
145
|
+
tags: [Users]
|
|
146
|
+
summary: Delete user
|
|
147
|
+
operationId: deleteUser
|
|
148
|
+
parameters:
|
|
149
|
+
- $ref: '#/components/parameters/UserIdParam'
|
|
150
|
+
responses:
|
|
151
|
+
'204':
|
|
152
|
+
description: User deleted
|
|
153
|
+
'404':
|
|
154
|
+
$ref: '#/components/responses/NotFound'
|
|
155
|
+
security:
|
|
156
|
+
- BearerAuth: []
|
|
157
|
+
|
|
158
|
+
/auth/login:
|
|
159
|
+
post:
|
|
160
|
+
tags: [Auth]
|
|
161
|
+
summary: Login
|
|
162
|
+
operationId: login
|
|
163
|
+
requestBody:
|
|
164
|
+
required: true
|
|
165
|
+
content:
|
|
166
|
+
application/json:
|
|
167
|
+
schema:
|
|
168
|
+
type: object
|
|
169
|
+
required: [email, password]
|
|
170
|
+
properties:
|
|
171
|
+
email:
|
|
172
|
+
type: string
|
|
173
|
+
format: email
|
|
174
|
+
password:
|
|
175
|
+
type: string
|
|
176
|
+
format: password
|
|
177
|
+
responses:
|
|
178
|
+
'200':
|
|
179
|
+
description: Login successful
|
|
180
|
+
content:
|
|
181
|
+
application/json:
|
|
182
|
+
schema:
|
|
183
|
+
type: object
|
|
184
|
+
properties:
|
|
185
|
+
accessToken:
|
|
186
|
+
type: string
|
|
187
|
+
refreshToken:
|
|
188
|
+
type: string
|
|
189
|
+
expiresIn:
|
|
190
|
+
type: integer
|
|
191
|
+
'401':
|
|
192
|
+
description: Invalid credentials
|
|
193
|
+
|
|
194
|
+
components:
|
|
195
|
+
schemas:
|
|
196
|
+
User:
|
|
197
|
+
type: object
|
|
198
|
+
required: [id, email, name, createdAt]
|
|
199
|
+
properties:
|
|
200
|
+
id:
|
|
201
|
+
type: string
|
|
202
|
+
format: uuid
|
|
203
|
+
readOnly: true
|
|
204
|
+
email:
|
|
205
|
+
type: string
|
|
206
|
+
format: email
|
|
207
|
+
name:
|
|
208
|
+
type: string
|
|
209
|
+
minLength: 2
|
|
210
|
+
maxLength: 100
|
|
211
|
+
bio:
|
|
212
|
+
type: string
|
|
213
|
+
maxLength: 500
|
|
214
|
+
nullable: true
|
|
215
|
+
role:
|
|
216
|
+
type: string
|
|
217
|
+
enum: [user, admin, moderator]
|
|
218
|
+
default: user
|
|
219
|
+
status:
|
|
220
|
+
type: string
|
|
221
|
+
enum: [active, inactive, pending]
|
|
222
|
+
createdAt:
|
|
223
|
+
type: string
|
|
224
|
+
format: date-time
|
|
225
|
+
readOnly: true
|
|
226
|
+
updatedAt:
|
|
227
|
+
type: string
|
|
228
|
+
format: date-time
|
|
229
|
+
readOnly: true
|
|
230
|
+
|
|
231
|
+
CreateUserInput:
|
|
232
|
+
type: object
|
|
233
|
+
required: [email, name, password]
|
|
234
|
+
properties:
|
|
235
|
+
email:
|
|
236
|
+
type: string
|
|
237
|
+
format: email
|
|
238
|
+
name:
|
|
239
|
+
type: string
|
|
240
|
+
minLength: 2
|
|
241
|
+
password:
|
|
242
|
+
type: string
|
|
243
|
+
format: password
|
|
244
|
+
minLength: 8
|
|
245
|
+
|
|
246
|
+
UpdateUserInput:
|
|
247
|
+
type: object
|
|
248
|
+
properties:
|
|
249
|
+
name:
|
|
250
|
+
type: string
|
|
251
|
+
minLength: 2
|
|
252
|
+
bio:
|
|
253
|
+
type: string
|
|
254
|
+
maxLength: 500
|
|
255
|
+
|
|
256
|
+
UserResponse:
|
|
257
|
+
type: object
|
|
258
|
+
properties:
|
|
259
|
+
data:
|
|
260
|
+
$ref: '#/components/schemas/User'
|
|
261
|
+
|
|
262
|
+
UserListResponse:
|
|
263
|
+
type: object
|
|
264
|
+
properties:
|
|
265
|
+
data:
|
|
266
|
+
type: array
|
|
267
|
+
items:
|
|
268
|
+
$ref: '#/components/schemas/User'
|
|
269
|
+
meta:
|
|
270
|
+
$ref: '#/components/schemas/PaginationMeta'
|
|
271
|
+
|
|
272
|
+
PaginationMeta:
|
|
273
|
+
type: object
|
|
274
|
+
properties:
|
|
275
|
+
page:
|
|
276
|
+
type: integer
|
|
277
|
+
limit:
|
|
278
|
+
type: integer
|
|
279
|
+
total:
|
|
280
|
+
type: integer
|
|
281
|
+
totalPages:
|
|
282
|
+
type: integer
|
|
283
|
+
|
|
284
|
+
Error:
|
|
285
|
+
type: object
|
|
286
|
+
required: [code, message]
|
|
287
|
+
properties:
|
|
288
|
+
code:
|
|
289
|
+
type: string
|
|
290
|
+
message:
|
|
291
|
+
type: string
|
|
292
|
+
details:
|
|
293
|
+
type: array
|
|
294
|
+
items:
|
|
295
|
+
type: object
|
|
296
|
+
properties:
|
|
297
|
+
field:
|
|
298
|
+
type: string
|
|
299
|
+
message:
|
|
300
|
+
type: string
|
|
301
|
+
|
|
302
|
+
parameters:
|
|
303
|
+
UserIdParam:
|
|
304
|
+
name: id
|
|
305
|
+
in: path
|
|
306
|
+
required: true
|
|
307
|
+
schema:
|
|
308
|
+
type: string
|
|
309
|
+
format: uuid
|
|
310
|
+
description: User ID
|
|
311
|
+
|
|
312
|
+
PageParam:
|
|
313
|
+
name: page
|
|
314
|
+
in: query
|
|
315
|
+
schema:
|
|
316
|
+
type: integer
|
|
317
|
+
minimum: 1
|
|
318
|
+
default: 1
|
|
319
|
+
|
|
320
|
+
LimitParam:
|
|
321
|
+
name: limit
|
|
322
|
+
in: query
|
|
323
|
+
schema:
|
|
324
|
+
type: integer
|
|
325
|
+
minimum: 1
|
|
326
|
+
maximum: 100
|
|
327
|
+
default: 20
|
|
328
|
+
|
|
329
|
+
responses:
|
|
330
|
+
NotFound:
|
|
331
|
+
description: Resource not found
|
|
332
|
+
content:
|
|
333
|
+
application/json:
|
|
334
|
+
schema:
|
|
335
|
+
$ref: '#/components/schemas/Error'
|
|
336
|
+
example:
|
|
337
|
+
code: NOT_FOUND
|
|
338
|
+
message: Resource not found
|
|
339
|
+
|
|
340
|
+
Unauthorized:
|
|
341
|
+
description: Authentication required
|
|
342
|
+
content:
|
|
343
|
+
application/json:
|
|
344
|
+
schema:
|
|
345
|
+
$ref: '#/components/schemas/Error'
|
|
346
|
+
|
|
347
|
+
ValidationError:
|
|
348
|
+
description: Validation failed
|
|
349
|
+
content:
|
|
350
|
+
application/json:
|
|
351
|
+
schema:
|
|
352
|
+
$ref: '#/components/schemas/Error'
|
|
353
|
+
|
|
354
|
+
securitySchemes:
|
|
355
|
+
BearerAuth:
|
|
356
|
+
type: http
|
|
357
|
+
scheme: bearer
|
|
358
|
+
bearerFormat: JWT
|
|
359
|
+
|
|
360
|
+
ApiKeyAuth:
|
|
361
|
+
type: apiKey
|
|
362
|
+
in: header
|
|
363
|
+
name: X-API-Key
|
|
364
|
+
|
|
365
|
+
OAuth2:
|
|
366
|
+
type: oauth2
|
|
367
|
+
flows:
|
|
368
|
+
authorizationCode:
|
|
369
|
+
authorizationUrl: https://auth.example.com/authorize
|
|
370
|
+
tokenUrl: https://auth.example.com/token
|
|
371
|
+
scopes:
|
|
372
|
+
read:users: Read user data
|
|
373
|
+
write:users: Modify user data
|
|
374
|
+
\`\`\`
|
|
375
|
+
|
|
376
|
+
## Code Generation
|
|
377
|
+
\`\`\`bash
|
|
378
|
+
# Generate TypeScript types
|
|
379
|
+
npx openapi-typescript openapi.yaml -o types/api.d.ts
|
|
380
|
+
|
|
381
|
+
# Generate client SDK
|
|
382
|
+
npx @openapitools/openapi-generator-cli generate \\
|
|
383
|
+
-i openapi.yaml \\
|
|
384
|
+
-g typescript-fetch \\
|
|
385
|
+
-o src/api-client
|
|
386
|
+
|
|
387
|
+
# Generate server stubs
|
|
388
|
+
npx @openapitools/openapi-generator-cli generate \\
|
|
389
|
+
-i openapi.yaml \\
|
|
390
|
+
-g typescript-express-server \\
|
|
391
|
+
-o src/server
|
|
392
|
+
|
|
393
|
+
# Validate spec
|
|
394
|
+
npx @redocly/cli lint openapi.yaml
|
|
395
|
+
\`\`\`
|
|
396
|
+
|
|
397
|
+
## TypeScript Integration
|
|
398
|
+
\`\`\`typescript
|
|
399
|
+
// Using generated types
|
|
400
|
+
import type { paths, components } from './types/api';
|
|
401
|
+
|
|
402
|
+
type User = components['schemas']['User'];
|
|
403
|
+
type CreateUserInput = components['schemas']['CreateUserInput'];
|
|
404
|
+
|
|
405
|
+
// Type-safe fetch with openapi-fetch
|
|
406
|
+
import createClient from 'openapi-fetch';
|
|
407
|
+
import type { paths } from './types/api';
|
|
408
|
+
|
|
409
|
+
const client = createClient<paths>({ baseUrl: '/api' });
|
|
410
|
+
|
|
411
|
+
// Fully typed request/response
|
|
412
|
+
const { data, error } = await client.GET('/users/{id}', {
|
|
413
|
+
params: { path: { id: '123' } },
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// data is typed as User | undefined
|
|
417
|
+
// error is typed as Error | undefined
|
|
418
|
+
\`\`\`
|
|
419
|
+
|
|
420
|
+
## Schema Patterns
|
|
421
|
+
\`\`\`yaml
|
|
422
|
+
# Inheritance with allOf
|
|
423
|
+
components:
|
|
424
|
+
schemas:
|
|
425
|
+
BaseEntity:
|
|
426
|
+
type: object
|
|
427
|
+
properties:
|
|
428
|
+
id:
|
|
429
|
+
type: string
|
|
430
|
+
format: uuid
|
|
431
|
+
createdAt:
|
|
432
|
+
type: string
|
|
433
|
+
format: date-time
|
|
434
|
+
|
|
435
|
+
User:
|
|
436
|
+
allOf:
|
|
437
|
+
- $ref: '#/components/schemas/BaseEntity'
|
|
438
|
+
- type: object
|
|
439
|
+
properties:
|
|
440
|
+
email:
|
|
441
|
+
type: string
|
|
442
|
+
|
|
443
|
+
# Polymorphism with oneOf
|
|
444
|
+
Pet:
|
|
445
|
+
oneOf:
|
|
446
|
+
- $ref: '#/components/schemas/Cat'
|
|
447
|
+
- $ref: '#/components/schemas/Dog'
|
|
448
|
+
discriminator:
|
|
449
|
+
propertyName: type
|
|
450
|
+
mapping:
|
|
451
|
+
cat: '#/components/schemas/Cat'
|
|
452
|
+
dog: '#/components/schemas/Dog'
|
|
453
|
+
|
|
454
|
+
# Nullable fields
|
|
455
|
+
Profile:
|
|
456
|
+
type: object
|
|
457
|
+
properties:
|
|
458
|
+
bio:
|
|
459
|
+
type: string
|
|
460
|
+
nullable: true
|
|
461
|
+
\`\`\`
|
|
462
|
+
|
|
463
|
+
## Documentation Tools
|
|
464
|
+
\`\`\`bash
|
|
465
|
+
# Swagger UI
|
|
466
|
+
npx swagger-ui-express # Express middleware
|
|
467
|
+
|
|
468
|
+
# Redoc (static docs)
|
|
469
|
+
npx @redocly/cli build-docs openapi.yaml -o docs/index.html
|
|
470
|
+
|
|
471
|
+
# Stoplight Elements (React component)
|
|
472
|
+
npm install @stoplight/elements
|
|
473
|
+
\`\`\`
|
|
474
|
+
|
|
475
|
+
## ❌ DON'T
|
|
476
|
+
- Skip required fields in schemas
|
|
477
|
+
- Use generic names (data, item, object)
|
|
478
|
+
- Forget error responses
|
|
479
|
+
- Leave descriptions empty
|
|
480
|
+
- Use inconsistent naming conventions
|
|
481
|
+
|
|
482
|
+
## ✅ DO
|
|
483
|
+
- Use \$ref for reusable components
|
|
484
|
+
- Include examples for all schemas
|
|
485
|
+
- Document all error responses
|
|
486
|
+
- Add operationId for code generation
|
|
487
|
+
- Use semantic versioning
|
|
488
|
+
- Validate spec before deployment
|
|
489
|
+
- Generate types from spec
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Performance Skill
|
|
2
|
+
|
|
3
|
+
## Core Web Vitals
|
|
4
|
+
\`\`\`
|
|
5
|
+
LCP (Largest Contentful Paint): < 2.5s
|
|
6
|
+
- Optimize images, fonts, critical CSS
|
|
7
|
+
- Preload key resources
|
|
8
|
+
|
|
9
|
+
FID (First Input Delay): < 100ms
|
|
10
|
+
- Minimize JavaScript execution
|
|
11
|
+
- Break up long tasks
|
|
12
|
+
|
|
13
|
+
CLS (Cumulative Layout Shift): < 0.1
|
|
14
|
+
- Set dimensions on images/videos
|
|
15
|
+
- Avoid inserting content above existing content
|
|
16
|
+
\`\`\`
|
|
17
|
+
|
|
18
|
+
## Code Splitting & Lazy Loading
|
|
19
|
+
\`\`\`tsx
|
|
20
|
+
// ✅ React lazy loading
|
|
21
|
+
import { lazy, Suspense } from 'react';
|
|
22
|
+
|
|
23
|
+
const Dashboard = lazy(() => import('./Dashboard'));
|
|
24
|
+
const Settings = lazy(() => import('./Settings'));
|
|
25
|
+
|
|
26
|
+
function App() {
|
|
27
|
+
return (
|
|
28
|
+
<Suspense fallback={<Loading />}>
|
|
29
|
+
<Routes>
|
|
30
|
+
<Route path="/dashboard" element={<Dashboard />} />
|
|
31
|
+
<Route path="/settings" element={<Settings />} />
|
|
32
|
+
</Routes>
|
|
33
|
+
</Suspense>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ✅ Next.js dynamic import
|
|
38
|
+
import dynamic from 'next/dynamic';
|
|
39
|
+
|
|
40
|
+
const HeavyChart = dynamic(() => import('./HeavyChart'), {
|
|
41
|
+
loading: () => <Skeleton />,
|
|
42
|
+
ssr: false // Skip server-side rendering
|
|
43
|
+
});
|
|
44
|
+
\`\`\`
|
|
45
|
+
|
|
46
|
+
## Image Optimization
|
|
47
|
+
\`\`\`tsx
|
|
48
|
+
// ✅ Next.js Image (automatic optimization)
|
|
49
|
+
import Image from 'next/image';
|
|
50
|
+
|
|
51
|
+
<Image
|
|
52
|
+
src="/hero.jpg"
|
|
53
|
+
width={1200}
|
|
54
|
+
height={600}
|
|
55
|
+
alt="Hero"
|
|
56
|
+
priority // Preload above-the-fold images
|
|
57
|
+
placeholder="blur"
|
|
58
|
+
blurDataURL={blurHash}
|
|
59
|
+
/>
|
|
60
|
+
|
|
61
|
+
// ✅ Responsive images
|
|
62
|
+
<Image
|
|
63
|
+
src="/photo.jpg"
|
|
64
|
+
sizes="(max-width: 768px) 100vw, 50vw"
|
|
65
|
+
fill
|
|
66
|
+
style={{ objectFit: 'cover' }}
|
|
67
|
+
/>
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
## React Memoization
|
|
71
|
+
\`\`\`tsx
|
|
72
|
+
// ✅ Memoize expensive computations
|
|
73
|
+
const sortedItems = useMemo(
|
|
74
|
+
() => items.sort((a, b) => b.score - a.score),
|
|
75
|
+
[items]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// ✅ Memoize callbacks passed to children
|
|
79
|
+
const handleClick = useCallback(
|
|
80
|
+
(id: string) => dispatch({ type: 'SELECT', id }),
|
|
81
|
+
[dispatch]
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// ✅ Memoize components that receive objects/arrays
|
|
85
|
+
const MemoizedList = memo(function List({ items }: Props) {
|
|
86
|
+
return items.map(item => <Item key={item.id} {...item} />);
|
|
87
|
+
});
|
|
88
|
+
\`\`\`
|
|
89
|
+
|
|
90
|
+
## Database Performance
|
|
91
|
+
\`\`\`typescript
|
|
92
|
+
// ❌ N+1 Problem
|
|
93
|
+
const users = await prisma.user.findMany();
|
|
94
|
+
for (const user of users) {
|
|
95
|
+
const posts = await prisma.post.findMany({ where: { authorId: user.id } });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ✅ Single query with include
|
|
99
|
+
const users = await prisma.user.findMany({
|
|
100
|
+
include: { posts: true }
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ✅ Select only needed fields
|
|
104
|
+
const users = await prisma.user.findMany({
|
|
105
|
+
select: { id: true, name: true, email: true }
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ✅ Add database indexes
|
|
109
|
+
// schema.prisma
|
|
110
|
+
model User {
|
|
111
|
+
id String @id
|
|
112
|
+
email String @unique
|
|
113
|
+
name String
|
|
114
|
+
|
|
115
|
+
@@index([name]) // Add index for frequent lookups
|
|
116
|
+
}
|
|
117
|
+
\`\`\`
|
|
118
|
+
|
|
119
|
+
## Redis Caching
|
|
120
|
+
\`\`\`typescript
|
|
121
|
+
import Redis from 'ioredis';
|
|
122
|
+
|
|
123
|
+
const redis = new Redis(process.env.REDIS_URL);
|
|
124
|
+
|
|
125
|
+
async function getUser(id: string) {
|
|
126
|
+
// Check cache first
|
|
127
|
+
const cached = await redis.get(\`user:\${id}\`);
|
|
128
|
+
if (cached) return JSON.parse(cached);
|
|
129
|
+
|
|
130
|
+
// Fetch from DB
|
|
131
|
+
const user = await prisma.user.findUnique({ where: { id } });
|
|
132
|
+
|
|
133
|
+
// Cache for 1 hour
|
|
134
|
+
await redis.set(\`user:\${id}\`, JSON.stringify(user), 'EX', 3600);
|
|
135
|
+
|
|
136
|
+
return user;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Invalidate on update
|
|
140
|
+
async function updateUser(id: string, data: UpdateData) {
|
|
141
|
+
await prisma.user.update({ where: { id }, data });
|
|
142
|
+
await redis.del(\`user:\${id}\`); // Clear cache
|
|
143
|
+
}
|
|
144
|
+
\`\`\`
|
|
145
|
+
|
|
146
|
+
## Bundle Analysis
|
|
147
|
+
\`\`\`bash
|
|
148
|
+
# Next.js
|
|
149
|
+
ANALYZE=true npm run build
|
|
150
|
+
|
|
151
|
+
# Webpack (add to package.json)
|
|
152
|
+
npx webpack-bundle-analyzer stats.json
|
|
153
|
+
|
|
154
|
+
# Vite
|
|
155
|
+
npx vite-bundle-visualizer
|
|
156
|
+
\`\`\`
|
|
157
|
+
|
|
158
|
+
## Performance Budgets
|
|
159
|
+
\`\`\`json
|
|
160
|
+
// package.json or bundlesize config
|
|
161
|
+
{
|
|
162
|
+
"bundlesize": [
|
|
163
|
+
{ "path": "./dist/main.js", "maxSize": "150 kB" },
|
|
164
|
+
{ "path": "./dist/vendor.js", "maxSize": "300 kB" }
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
\`\`\`
|
|
168
|
+
|
|
169
|
+
## Monitoring
|
|
170
|
+
\`\`\`typescript
|
|
171
|
+
// Send Web Vitals to analytics
|
|
172
|
+
import { onCLS, onFID, onLCP } from 'web-vitals';
|
|
173
|
+
|
|
174
|
+
function sendToAnalytics({ name, delta, id }) {
|
|
175
|
+
fetch('/api/vitals', {
|
|
176
|
+
method: 'POST',
|
|
177
|
+
body: JSON.stringify({ name, delta, id })
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
onCLS(sendToAnalytics);
|
|
182
|
+
onFID(sendToAnalytics);
|
|
183
|
+
onLCP(sendToAnalytics);
|
|
184
|
+
\`\`\`
|
|
185
|
+
|
|
186
|
+
## ❌ DON'T
|
|
187
|
+
- Optimize without measuring
|
|
188
|
+
- Load unused JavaScript
|
|
189
|
+
- Use large unoptimized images
|
|
190
|
+
- Forget to cache database queries
|
|
191
|
+
- Skip lazy loading for heavy components
|
|
192
|
+
|
|
193
|
+
## ✅ DO
|
|
194
|
+
- Measure before optimizing
|
|
195
|
+
- Set performance budgets
|
|
196
|
+
- Use caching strategically
|
|
197
|
+
- Optimize critical path first
|
|
198
|
+
- Monitor Core Web Vitals
|
|
199
|
+
- Use CDN for static assets
|