@musashishao/agent-kit 1.8.1 → 1.9.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/.agent/agents/ai-architect.md +39 -0
- package/.agent/agents/cloud-engineer.md +39 -0
- package/.agent/agents/game-asset-curator.md +317 -0
- package/.agent/agents/game-developer.md +190 -89
- package/.agent/agents/game-narrative-designer.md +310 -0
- package/.agent/agents/game-qa-agent.md +441 -0
- package/.agent/agents/marketing-specialist.md +41 -0
- package/.agent/agents/penetration-tester.md +15 -1
- package/.agent/rules/CODEX.md +26 -2
- package/.agent/rules/GEMINI.md +7 -5
- package/.agent/rules/REFERENCE.md +92 -2
- package/.agent/scripts/ak_cli.py +1 -1
- package/.agent/scripts/localize_workflows.py +54 -0
- package/.agent/scripts/memory_manager.py +24 -1
- package/.agent/skills/3d-web-experience/SKILL.md +386 -0
- package/.agent/skills/DEPENDENCIES.md +54 -0
- package/.agent/skills/ab-test-setup/SKILL.md +77 -0
- package/.agent/skills/active-directory-attacks/SKILL.md +59 -0
- package/.agent/skills/agent-evaluation/SKILL.md +430 -0
- package/.agent/skills/agent-memory-systems/SKILL.md +426 -0
- package/.agent/skills/agent-tool-builder/SKILL.md +139 -0
- package/.agent/skills/ai-agents-architect/SKILL.md +115 -0
- package/.agent/skills/ai-product/SKILL.md +86 -0
- package/.agent/skills/ai-wrapper-product/SKILL.md +90 -0
- package/.agent/skills/analytics-tracking/SKILL.md +88 -0
- package/.agent/skills/api-fuzzing-bug-bounty/SKILL.md +66 -0
- package/.agent/skills/app-store-optimization/SKILL.md +66 -0
- package/.agent/skills/autonomous-agent-patterns/SKILL.md +414 -0
- package/.agent/skills/aws-penetration-testing/SKILL.md +50 -0
- package/.agent/skills/aws-serverless/SKILL.md +327 -0
- package/.agent/skills/azure-functions/SKILL.md +340 -0
- package/.agent/skills/broken-authentication/SKILL.md +53 -0
- package/.agent/skills/browser-automation/SKILL.md +408 -0
- package/.agent/skills/browser-extension-builder/SKILL.md +422 -0
- package/.agent/skills/bullmq-specialist/SKILL.md +424 -0
- package/.agent/skills/bun-development/SKILL.md +386 -0
- package/.agent/skills/burp-suite-testing/SKILL.md +60 -0
- package/.agent/skills/clerk-auth/SKILL.md +432 -0
- package/.agent/skills/cloud-penetration-testing/SKILL.md +51 -0
- package/.agent/skills/copywriting/SKILL.md +66 -0
- package/.agent/skills/crewai/SKILL.md +470 -0
- package/.agent/skills/discord-bot-architect/SKILL.md +447 -0
- package/.agent/skills/email-sequence/SKILL.md +73 -0
- package/.agent/skills/ethical-hacking-methodology/SKILL.md +67 -0
- package/.agent/skills/firebase/SKILL.md +377 -0
- package/.agent/skills/game-development/godot-expert/SKILL.md +462 -0
- package/.agent/skills/game-development/npc-ai-integration/SKILL.md +110 -0
- package/.agent/skills/game-development/procedural-generation/SKILL.md +168 -0
- package/.agent/skills/game-development/unity-integration/SKILL.md +358 -0
- package/.agent/skills/game-development/webgpu-shading/SKILL.md +209 -0
- package/.agent/skills/gcp-cloud-run/SKILL.md +358 -0
- package/.agent/skills/graphql/SKILL.md +492 -0
- package/.agent/skills/idor-testing/SKILL.md +64 -0
- package/.agent/skills/inngest/SKILL.md +128 -0
- package/.agent/skills/langfuse/SKILL.md +415 -0
- package/.agent/skills/langgraph/SKILL.md +360 -0
- package/.agent/skills/launch-strategy/SKILL.md +68 -0
- package/.agent/skills/linux-privilege-escalation/SKILL.md +62 -0
- package/.agent/skills/llm-app-patterns/SKILL.md +367 -0
- package/.agent/skills/marketing-ideas/SKILL.md +66 -0
- package/.agent/skills/metasploit-framework/SKILL.md +60 -0
- package/.agent/skills/micro-saas-launcher/SKILL.md +93 -0
- package/.agent/skills/neon-postgres/SKILL.md +339 -0
- package/.agent/skills/paid-ads/SKILL.md +64 -0
- package/.agent/skills/supabase-integration/SKILL.md +411 -0
- package/.agent/workflows/ai-agent.md +36 -0
- package/.agent/workflows/autofix.md +1 -0
- package/.agent/workflows/brainstorm.md +1 -0
- package/.agent/workflows/context.md +1 -0
- package/.agent/workflows/create.md +1 -0
- package/.agent/workflows/dashboard.md +1 -0
- package/.agent/workflows/debug.md +1 -0
- package/.agent/workflows/deploy.md +1 -0
- package/.agent/workflows/enhance.md +1 -0
- package/.agent/workflows/game-prototype.md +154 -0
- package/.agent/workflows/marketing.md +37 -0
- package/.agent/workflows/next.md +1 -0
- package/.agent/workflows/orchestrate.md +1 -0
- package/.agent/workflows/pentest.md +37 -0
- package/.agent/workflows/plan.md +1 -0
- package/.agent/workflows/preview.md +2 -1
- package/.agent/workflows/quality.md +1 -0
- package/.agent/workflows/saas.md +36 -0
- package/.agent/workflows/spec.md +1 -0
- package/.agent/workflows/status.md +1 -0
- package/.agent/workflows/test.md +1 -0
- package/.agent/workflows/ui-ux-pro-max.md +1 -0
- package/README.md +52 -24
- package/bin/cli.js +68 -3
- package/docs/CHANGELOG_AI_INFRA.md +30 -0
- package/docs/MIGRATION_GUIDE_V1.9.md +55 -0
- package/package.json +1 -1
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: graphql
|
|
3
|
+
description: "GraphQL gives clients exactly the data they need. Covers schema design, resolvers, DataLoader for N+1 prevention, federation, and client integration with Apollo/urql."
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
source: "antigravity-awesome-skills (adapted)"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# 🔮 GraphQL
|
|
9
|
+
|
|
10
|
+
You're a developer who has built GraphQL APIs at scale. You've seen the N+1 query problem bring down production servers. You know that GraphQL's power is also its danger.
|
|
11
|
+
|
|
12
|
+
**Key insight**: GraphQL is a contract. The schema is the API documentation. Design it carefully.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## When to Use This Skill
|
|
17
|
+
|
|
18
|
+
- Building APIs with complex data requirements
|
|
19
|
+
- Clients need flexible data fetching
|
|
20
|
+
- Multiple frontends (web, mobile) sharing API
|
|
21
|
+
- Microservices needing unified graph
|
|
22
|
+
- Real-time subscriptions
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Capabilities
|
|
27
|
+
|
|
28
|
+
- `graphql-schema-design`
|
|
29
|
+
- `graphql-resolvers`
|
|
30
|
+
- `graphql-federation`
|
|
31
|
+
- `graphql-subscriptions`
|
|
32
|
+
- `graphql-dataloader`
|
|
33
|
+
- `graphql-codegen`
|
|
34
|
+
- `apollo-server`
|
|
35
|
+
- `apollo-client`
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 1. Schema Design
|
|
40
|
+
|
|
41
|
+
### Type-Safe Schema
|
|
42
|
+
|
|
43
|
+
```graphql
|
|
44
|
+
# schema.graphql
|
|
45
|
+
|
|
46
|
+
# Custom scalars
|
|
47
|
+
scalar DateTime
|
|
48
|
+
scalar UUID
|
|
49
|
+
|
|
50
|
+
# Enums
|
|
51
|
+
enum OrderStatus {
|
|
52
|
+
PENDING
|
|
53
|
+
PROCESSING
|
|
54
|
+
SHIPPED
|
|
55
|
+
DELIVERED
|
|
56
|
+
CANCELLED
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Types with relations
|
|
60
|
+
type User {
|
|
61
|
+
id: UUID!
|
|
62
|
+
email: String!
|
|
63
|
+
name: String
|
|
64
|
+
avatar: String
|
|
65
|
+
createdAt: DateTime!
|
|
66
|
+
orders: [Order!]!
|
|
67
|
+
ordersCount: Int!
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type Order {
|
|
71
|
+
id: UUID!
|
|
72
|
+
status: OrderStatus!
|
|
73
|
+
total: Float!
|
|
74
|
+
items: [OrderItem!]!
|
|
75
|
+
user: User!
|
|
76
|
+
createdAt: DateTime!
|
|
77
|
+
updatedAt: DateTime!
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type OrderItem {
|
|
81
|
+
id: UUID!
|
|
82
|
+
quantity: Int!
|
|
83
|
+
unitPrice: Float!
|
|
84
|
+
product: Product!
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type Product {
|
|
88
|
+
id: UUID!
|
|
89
|
+
name: String!
|
|
90
|
+
description: String
|
|
91
|
+
price: Float!
|
|
92
|
+
inventory: Int!
|
|
93
|
+
category: Category!
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Paginated response
|
|
97
|
+
type UserConnection {
|
|
98
|
+
edges: [UserEdge!]!
|
|
99
|
+
pageInfo: PageInfo!
|
|
100
|
+
totalCount: Int!
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
type UserEdge {
|
|
104
|
+
node: User!
|
|
105
|
+
cursor: String!
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
type PageInfo {
|
|
109
|
+
hasNextPage: Boolean!
|
|
110
|
+
hasPreviousPage: Boolean!
|
|
111
|
+
startCursor: String
|
|
112
|
+
endCursor: String
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# Queries
|
|
116
|
+
type Query {
|
|
117
|
+
user(id: UUID!): User
|
|
118
|
+
users(first: Int, after: String): UserConnection!
|
|
119
|
+
order(id: UUID!): Order
|
|
120
|
+
products(category: String, limit: Int = 20): [Product!]!
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Mutations
|
|
124
|
+
type Mutation {
|
|
125
|
+
createUser(input: CreateUserInput!): User!
|
|
126
|
+
updateUser(id: UUID!, input: UpdateUserInput!): User!
|
|
127
|
+
createOrder(input: CreateOrderInput!): Order!
|
|
128
|
+
cancelOrder(id: UUID!): Order!
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# Input types
|
|
132
|
+
input CreateUserInput {
|
|
133
|
+
email: String!
|
|
134
|
+
name: String
|
|
135
|
+
password: String!
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
input UpdateUserInput {
|
|
139
|
+
name: String
|
|
140
|
+
avatar: String
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
input CreateOrderInput {
|
|
144
|
+
items: [OrderItemInput!]!
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
input OrderItemInput {
|
|
148
|
+
productId: UUID!
|
|
149
|
+
quantity: Int!
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Subscriptions
|
|
153
|
+
type Subscription {
|
|
154
|
+
orderStatusChanged(orderId: UUID!): Order!
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 2. Resolvers with DataLoader
|
|
161
|
+
|
|
162
|
+
### DataLoader for N+1 Prevention
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// dataloaders.ts
|
|
166
|
+
import DataLoader from 'dataloader';
|
|
167
|
+
import { prisma } from './prisma';
|
|
168
|
+
|
|
169
|
+
export function createLoaders() {
|
|
170
|
+
return {
|
|
171
|
+
user: new DataLoader<string, User>(async (ids) => {
|
|
172
|
+
const users = await prisma.user.findMany({
|
|
173
|
+
where: { id: { in: [...ids] } }
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Map results to match input order
|
|
177
|
+
const userMap = new Map(users.map(u => [u.id, u]));
|
|
178
|
+
return ids.map(id => userMap.get(id) || null);
|
|
179
|
+
}),
|
|
180
|
+
|
|
181
|
+
product: new DataLoader<string, Product>(async (ids) => {
|
|
182
|
+
const products = await prisma.product.findMany({
|
|
183
|
+
where: { id: { in: [...ids] } }
|
|
184
|
+
});
|
|
185
|
+
const productMap = new Map(products.map(p => [p.id, p]));
|
|
186
|
+
return ids.map(id => productMap.get(id) || null);
|
|
187
|
+
}),
|
|
188
|
+
|
|
189
|
+
// Batch load related items
|
|
190
|
+
orderItems: new DataLoader<string, OrderItem[]>(async (orderIds) => {
|
|
191
|
+
const items = await prisma.orderItem.findMany({
|
|
192
|
+
where: { orderId: { in: [...orderIds] } }
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Group by orderId
|
|
196
|
+
const itemsByOrder = new Map<string, OrderItem[]>();
|
|
197
|
+
items.forEach(item => {
|
|
198
|
+
const existing = itemsByOrder.get(item.orderId) || [];
|
|
199
|
+
existing.push(item);
|
|
200
|
+
itemsByOrder.set(item.orderId, existing);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return orderIds.map(id => itemsByOrder.get(id) || []);
|
|
204
|
+
}),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export type Loaders = ReturnType<typeof createLoaders>;
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Resolvers
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// resolvers.ts
|
|
215
|
+
import { GraphQLContext } from './context';
|
|
216
|
+
|
|
217
|
+
export const resolvers = {
|
|
218
|
+
Query: {
|
|
219
|
+
user: async (_, { id }, ctx: GraphQLContext) => {
|
|
220
|
+
return ctx.loaders.user.load(id);
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
users: async (_, { first = 20, after }, ctx: GraphQLContext) => {
|
|
224
|
+
const cursor = after ? { id: after } : undefined;
|
|
225
|
+
|
|
226
|
+
const users = await ctx.prisma.user.findMany({
|
|
227
|
+
take: first + 1, // Fetch one extra for hasNextPage
|
|
228
|
+
cursor,
|
|
229
|
+
skip: cursor ? 1 : 0,
|
|
230
|
+
orderBy: { createdAt: 'desc' }
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const hasNextPage = users.length > first;
|
|
234
|
+
const edges = users.slice(0, first).map(user => ({
|
|
235
|
+
node: user,
|
|
236
|
+
cursor: user.id
|
|
237
|
+
}));
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
edges,
|
|
241
|
+
pageInfo: {
|
|
242
|
+
hasNextPage,
|
|
243
|
+
hasPreviousPage: !!after,
|
|
244
|
+
startCursor: edges[0]?.cursor,
|
|
245
|
+
endCursor: edges[edges.length - 1]?.cursor
|
|
246
|
+
},
|
|
247
|
+
totalCount: await ctx.prisma.user.count()
|
|
248
|
+
};
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
Mutation: {
|
|
253
|
+
createUser: async (_, { input }, ctx: GraphQLContext) => {
|
|
254
|
+
return ctx.prisma.user.create({
|
|
255
|
+
data: {
|
|
256
|
+
email: input.email,
|
|
257
|
+
name: input.name,
|
|
258
|
+
passwordHash: await hash(input.password)
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
createOrder: async (_, { input }, ctx: GraphQLContext) => {
|
|
264
|
+
// Verify auth
|
|
265
|
+
if (!ctx.user) {
|
|
266
|
+
throw new Error('Unauthorized');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return ctx.prisma.$transaction(async (tx) => {
|
|
270
|
+
// Calculate total
|
|
271
|
+
const products = await tx.product.findMany({
|
|
272
|
+
where: { id: { in: input.items.map(i => i.productId) } }
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const total = input.items.reduce((sum, item) => {
|
|
276
|
+
const product = products.find(p => p.id === item.productId);
|
|
277
|
+
return sum + (product?.price ?? 0) * item.quantity;
|
|
278
|
+
}, 0);
|
|
279
|
+
|
|
280
|
+
// Create order
|
|
281
|
+
return tx.order.create({
|
|
282
|
+
data: {
|
|
283
|
+
userId: ctx.user.id,
|
|
284
|
+
status: 'PENDING',
|
|
285
|
+
total,
|
|
286
|
+
items: {
|
|
287
|
+
create: input.items.map(item => ({
|
|
288
|
+
productId: item.productId,
|
|
289
|
+
quantity: item.quantity,
|
|
290
|
+
unitPrice: products.find(p => p.id === item.productId)?.price ?? 0
|
|
291
|
+
}))
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
// Field resolvers
|
|
300
|
+
User: {
|
|
301
|
+
orders: (user, _, ctx: GraphQLContext) => {
|
|
302
|
+
return ctx.prisma.order.findMany({
|
|
303
|
+
where: { userId: user.id }
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
ordersCount: (user, _, ctx: GraphQLContext) => {
|
|
307
|
+
return ctx.prisma.order.count({
|
|
308
|
+
where: { userId: user.id }
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
Order: {
|
|
314
|
+
user: (order, _, ctx: GraphQLContext) => {
|
|
315
|
+
return ctx.loaders.user.load(order.userId);
|
|
316
|
+
},
|
|
317
|
+
items: (order, _, ctx: GraphQLContext) => {
|
|
318
|
+
return ctx.loaders.orderItems.load(order.id);
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
OrderItem: {
|
|
323
|
+
product: (item, _, ctx: GraphQLContext) => {
|
|
324
|
+
return ctx.loaders.product.load(item.productId);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 3. Apollo Server Setup
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
// server.ts
|
|
336
|
+
import { ApolloServer } from '@apollo/server';
|
|
337
|
+
import { expressMiddleware } from '@apollo/server/express4';
|
|
338
|
+
import { createLoaders } from './dataloaders';
|
|
339
|
+
import { prisma } from './prisma';
|
|
340
|
+
import { typeDefs } from './schema';
|
|
341
|
+
import { resolvers } from './resolvers';
|
|
342
|
+
|
|
343
|
+
const server = new ApolloServer({
|
|
344
|
+
typeDefs,
|
|
345
|
+
resolvers,
|
|
346
|
+
plugins: [
|
|
347
|
+
// Disable introspection in production
|
|
348
|
+
...(process.env.NODE_ENV === 'production'
|
|
349
|
+
? [{ async serverWillStart() { return { async drainServer() {} }; } }]
|
|
350
|
+
: []),
|
|
351
|
+
],
|
|
352
|
+
validationRules: [
|
|
353
|
+
// Limit query depth
|
|
354
|
+
depthLimit(10),
|
|
355
|
+
// Limit query complexity
|
|
356
|
+
createComplexityLimitRule(1000),
|
|
357
|
+
],
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
await server.start();
|
|
361
|
+
|
|
362
|
+
app.use(
|
|
363
|
+
'/graphql',
|
|
364
|
+
expressMiddleware(server, {
|
|
365
|
+
context: async ({ req }) => {
|
|
366
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
367
|
+
const user = token ? await verifyToken(token) : null;
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
user,
|
|
371
|
+
prisma,
|
|
372
|
+
loaders: createLoaders(), // Fresh loaders per request
|
|
373
|
+
};
|
|
374
|
+
},
|
|
375
|
+
})
|
|
376
|
+
);
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## 4. Apollo Client Setup
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
// lib/apollo-client.ts
|
|
385
|
+
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
|
|
386
|
+
|
|
387
|
+
const httpLink = createHttpLink({
|
|
388
|
+
uri: process.env.NEXT_PUBLIC_GRAPHQL_URL,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
export const apolloClient = new ApolloClient({
|
|
392
|
+
link: httpLink,
|
|
393
|
+
cache: new InMemoryCache({
|
|
394
|
+
typePolicies: {
|
|
395
|
+
Query: {
|
|
396
|
+
fields: {
|
|
397
|
+
users: {
|
|
398
|
+
// Cursor-based pagination
|
|
399
|
+
keyArgs: false,
|
|
400
|
+
merge(existing, incoming, { args }) {
|
|
401
|
+
if (!args?.after) {
|
|
402
|
+
return incoming;
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
...incoming,
|
|
406
|
+
edges: [...(existing?.edges ?? []), ...incoming.edges],
|
|
407
|
+
};
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
}),
|
|
414
|
+
});
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## 5. Anti-Patterns
|
|
420
|
+
|
|
421
|
+
### ❌ No DataLoader
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
// WRONG: N+1 queries
|
|
425
|
+
const Order = {
|
|
426
|
+
items: async (order) => {
|
|
427
|
+
return prisma.orderItem.findMany({ where: { orderId: order.id } });
|
|
428
|
+
// 100 orders = 100 queries!
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
// CORRECT: Use DataLoader
|
|
433
|
+
const Order = {
|
|
434
|
+
items: (order, _, ctx) => ctx.loaders.orderItems.load(order.id)
|
|
435
|
+
// 100 orders = 1 batched query!
|
|
436
|
+
};
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### ❌ No Query Depth Limiting
|
|
440
|
+
|
|
441
|
+
```graphql
|
|
442
|
+
# WRONG: Allow unlimited depth
|
|
443
|
+
query DeeplyNested {
|
|
444
|
+
user(id: "1") {
|
|
445
|
+
orders {
|
|
446
|
+
items {
|
|
447
|
+
product {
|
|
448
|
+
category {
|
|
449
|
+
products {
|
|
450
|
+
# ... infinite nesting
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### ❌ Introspection in Production
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
// WRONG: Schema exposed in production
|
|
464
|
+
const server = new ApolloServer({ typeDefs, resolvers });
|
|
465
|
+
|
|
466
|
+
// CORRECT: Disable introspection
|
|
467
|
+
const server = new ApolloServer({
|
|
468
|
+
typeDefs,
|
|
469
|
+
resolvers,
|
|
470
|
+
introspection: process.env.NODE_ENV !== 'production',
|
|
471
|
+
});
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## 6. Sharp Edges
|
|
477
|
+
|
|
478
|
+
| Issue | Severity | Solution |
|
|
479
|
+
|-------|----------|----------|
|
|
480
|
+
| N+1 queries | Critical | USE DATALOADER |
|
|
481
|
+
| Deep nesting DoS | Critical | LIMIT DEPTH AND COMPLEXITY |
|
|
482
|
+
| Introspection exposed | High | DISABLE IN PRODUCTION |
|
|
483
|
+
| Auth only in directives | High | AUTHORIZE IN RESOLVERS |
|
|
484
|
+
| Non-null cascading nulls | Medium | DESIGN NULLABILITY |
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## Related Skills
|
|
489
|
+
|
|
490
|
+
- `api-patterns` - REST comparison
|
|
491
|
+
- `database-design` - Schema design
|
|
492
|
+
- `prisma-expert` - ORM integration
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: idor-testing
|
|
3
|
+
description: "Specialized skill for finding Insecure Direct Object Reference (IDOR) vulnerabilities. Covers parameter tampering, mass assignment, and BOLA (API)."
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 🔑 IDOR Testing
|
|
8
|
+
|
|
9
|
+
You are a Bug Bounty hunter who specializes in IDORs—the most common and impactful web vulnerability. You understand that IDOR is a logic flaw that scanners almost always miss.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What is IDOR?
|
|
14
|
+
When an application uses user-supplied input to access objects directly without an authorization check.
|
|
15
|
+
- **Example**: `/api/v1/orders/1001` -> works. Change to `/api/v1/orders/1002` -> access another user's order.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Advanced IDOR Patterns
|
|
20
|
+
|
|
21
|
+
### 1. Parameter Pollution
|
|
22
|
+
Standard IDOR: `?id=123`
|
|
23
|
+
Bypass: `?id=123&id=456` (Server might process the second one while checking the first).
|
|
24
|
+
|
|
25
|
+
### 2. ID Type Conversion
|
|
26
|
+
`GET /user/123` -> Forbidden.
|
|
27
|
+
Try: `GET /user/admin` or `GET /user/me`.
|
|
28
|
+
|
|
29
|
+
### 3. Path Traversal in API
|
|
30
|
+
`GET /api/users/123/profile` -> Forbidden.
|
|
31
|
+
Try: `GET /api/users/999/../123/profile`.
|
|
32
|
+
|
|
33
|
+
### 4. Wrapped IDOR (JSON)
|
|
34
|
+
```json
|
|
35
|
+
// Original
|
|
36
|
+
{ "user_id": 123 }
|
|
37
|
+
|
|
38
|
+
// Attempt
|
|
39
|
+
{ "user_id": [123, 456] }
|
|
40
|
+
{ "user_id": { "id": 456 } }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Broken Object Level Authorization (BOLA)
|
|
46
|
+
The API version of IDOR.
|
|
47
|
+
- **Technique**: Use a GUID instead of an Integer.
|
|
48
|
+
- **Bypass**: Where do you get the GUIDs? Try public profile pages, search results, or leak them from another API endpoint (e.g., `GET /api/v1/search?q=...`).
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Testing Workflow (The "Two User" Method)
|
|
53
|
+
1. Login as **User A** and **User B**.
|
|
54
|
+
2. Intercept User A's request to "Update Profile".
|
|
55
|
+
3. Replace User A's ID/Token with User B's ID/Token.
|
|
56
|
+
4. If User B's profile is updated while using User A's session -> **CRITICAL IDOR**.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Related Skills
|
|
61
|
+
|
|
62
|
+
- `burp-suite-testing` - The primary tool for IDOR
|
|
63
|
+
- `api-fuzzing-bug-bounty` - Scaling IDOR testing
|
|
64
|
+
- `broken-authentication` - Related auth flaws
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: inngest
|
|
3
|
+
description: "Expertise in building reliable, event-driven applications with Inngest. Covers durable functions, background jobs, and multi-step workflows."
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ⚡ Inngest
|
|
8
|
+
|
|
9
|
+
You are an expert in Inngest—the durable execution engine for TypeScript. You know how to build background jobs, delayed functions, and complex multi-step workflows without managing queues or cron infrastructure.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## When to Use This Skill
|
|
14
|
+
|
|
15
|
+
- Building background jobs in Next.js/Edge environments
|
|
16
|
+
- Implementing multi-step AI pipelines (e.g., Generate -> Review -> Notify)
|
|
17
|
+
- Setting up durable cron jobs
|
|
18
|
+
- Handling high-concurrency event processing
|
|
19
|
+
- Orchestrating complex user onboarding flows
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Capabilities
|
|
24
|
+
|
|
25
|
+
- `durable-functions`
|
|
26
|
+
- `event-driven-architecture`
|
|
27
|
+
- `step-functions-in-typescript`
|
|
28
|
+
- `background-job-management`
|
|
29
|
+
- `inngest-middleware`
|
|
30
|
+
- `fan-out-fan-in-patterns`
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 1. Defining Functions
|
|
35
|
+
|
|
36
|
+
Inngest functions are event-driven and durable by default.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { Inngest } from "inngest";
|
|
40
|
+
|
|
41
|
+
const inngest = new Inngest({ id: "my-app" });
|
|
42
|
+
|
|
43
|
+
export const helloWorld = inngest.createFunction(
|
|
44
|
+
{ id: "hello-world" },
|
|
45
|
+
{ event: "test/hello.world" },
|
|
46
|
+
async ({ event, step }) => {
|
|
47
|
+
await step.run("log-hello", () => {
|
|
48
|
+
console.log("Hello", event.data.name);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const result = await step.run("calculate", () => 2 + 2);
|
|
52
|
+
|
|
53
|
+
return { result };
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 2. Multi-Step AI Workflow
|
|
61
|
+
|
|
62
|
+
A common pattern for AI agents where steps might time out or need retries.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
export const processArticle = inngest.createFunction(
|
|
66
|
+
{ id: "process-article", retries: 3 },
|
|
67
|
+
{ event: "article/submitted" },
|
|
68
|
+
async ({ event, step }) => {
|
|
69
|
+
// 1. Generate Summary
|
|
70
|
+
const summary = await step.run("generate-summary", async () => {
|
|
71
|
+
return await llm.summarize(event.data.text);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// 2. Wait for a specific duration or external signal
|
|
75
|
+
await step.sleep("wait-for-review", "1h");
|
|
76
|
+
|
|
77
|
+
// 3. Notify user
|
|
78
|
+
await step.run("notify-user", async () => {
|
|
79
|
+
await resend.emails.send({
|
|
80
|
+
to: event.data.email,
|
|
81
|
+
subject: "Your summary is ready",
|
|
82
|
+
text: summary
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 3. Fan-Out Pattern (Batch Processing)
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
export const handleMigration = inngest.createFunction(
|
|
95
|
+
{ id: "master-migration" },
|
|
96
|
+
{ event: "db/migrate-all" },
|
|
97
|
+
async ({ step }) => {
|
|
98
|
+
const users = await step.run("fetch-users", () => db.users.findMany());
|
|
99
|
+
|
|
100
|
+
// Fan-out: create a separate function execution for each user
|
|
101
|
+
const events = users.map(user => ({
|
|
102
|
+
name: "db/user.migrate",
|
|
103
|
+
data: { userId: user.id }
|
|
104
|
+
}));
|
|
105
|
+
|
|
106
|
+
await step.sendEvent("send-migration-events", events);
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 4. Advanced Features
|
|
114
|
+
|
|
115
|
+
| Feature | Description |
|
|
116
|
+
|---------|-------------|
|
|
117
|
+
| **Concurrency** | Limit how many instances of a function run at once. |
|
|
118
|
+
| **Idempotency** | Ensure functions only process the same event once via `idempotencyKey`. |
|
|
119
|
+
| **Cancellation** | Stop a running workflow if a specific event is received (e.g., `order/cancelled`). |
|
|
120
|
+
| **Throttling** | Limit events over a duration per user. |
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Related Skills
|
|
125
|
+
|
|
126
|
+
- `api-patterns` - For event design
|
|
127
|
+
- `nodejs-best-practices` - General backend
|
|
128
|
+
- `bullmq-specialist` - Alternative for complex Redis-based queues
|