@ranimontagna/agent-toolkit 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +282 -277
  2. package/docs/assets/install-plan.svg +29 -0
  3. package/docs/assets/install-skill-packages.svg +31 -0
  4. package/docs/assets/install-status.svg +32 -0
  5. package/package.json +10 -9
  6. package/setup-agent-toolkit.sh +1 -1
  7. package/skills/backend/fastify-best-practices/LICENSE +21 -0
  8. package/skills/backend/fastify-best-practices/NOTICE.md +11 -0
  9. package/skills/backend/fastify-best-practices/SKILL.md +75 -0
  10. package/skills/backend/fastify-best-practices/rules/authentication.md +521 -0
  11. package/skills/backend/fastify-best-practices/rules/configuration.md +217 -0
  12. package/skills/backend/fastify-best-practices/rules/content-type.md +387 -0
  13. package/skills/backend/fastify-best-practices/rules/cors-security.md +445 -0
  14. package/skills/backend/fastify-best-practices/rules/database.md +320 -0
  15. package/skills/backend/fastify-best-practices/rules/decorators.md +416 -0
  16. package/skills/backend/fastify-best-practices/rules/deployment.md +423 -0
  17. package/skills/backend/fastify-best-practices/rules/error-handling.md +412 -0
  18. package/skills/backend/fastify-best-practices/rules/hooks.md +464 -0
  19. package/skills/backend/fastify-best-practices/rules/http-proxy.md +247 -0
  20. package/skills/backend/fastify-best-practices/rules/logging.md +402 -0
  21. package/skills/backend/fastify-best-practices/rules/performance.md +425 -0
  22. package/skills/backend/fastify-best-practices/rules/plugins.md +320 -0
  23. package/skills/backend/fastify-best-practices/rules/routes.md +467 -0
  24. package/skills/backend/fastify-best-practices/rules/schemas.md +585 -0
  25. package/skills/backend/fastify-best-practices/rules/serialization.md +475 -0
  26. package/skills/backend/fastify-best-practices/rules/testing.md +536 -0
  27. package/skills/backend/fastify-best-practices/rules/typescript.md +458 -0
  28. package/skills/backend/fastify-best-practices/rules/websockets.md +421 -0
  29. package/skills/backend/fastify-best-practices/tile.json +11 -0
  30. package/skills/core/agent-toolkit-maintainer/SKILL.md +16 -14
@@ -0,0 +1,458 @@
1
+ ---
2
+ name: typescript
3
+ description: TypeScript integration with Fastify
4
+ metadata:
5
+ tags: typescript, types, generics, type-safety
6
+ ---
7
+
8
+ # TypeScript Integration
9
+
10
+ ## Type Stripping with Node.js
11
+
12
+ Use Node.js built-in type stripping (Node.js 22.6+):
13
+
14
+ ```bash
15
+ # Run TypeScript directly
16
+ node --experimental-strip-types app.ts
17
+
18
+ # In Node.js 23+
19
+ node app.ts
20
+ ```
21
+
22
+ ```json
23
+ // package.json
24
+ {
25
+ "type": "module",
26
+ "scripts": {
27
+ "start": "node app.ts",
28
+ "dev": "node --watch app.ts"
29
+ }
30
+ }
31
+ ```
32
+
33
+ ```typescript
34
+ // tsconfig.json for type stripping
35
+ {
36
+ "compilerOptions": {
37
+ "target": "ESNext",
38
+ "module": "NodeNext",
39
+ "moduleResolution": "NodeNext",
40
+ "verbatimModuleSyntax": true,
41
+ "erasableSyntaxOnly": true,
42
+ "noEmit": true,
43
+ "strict": true
44
+ }
45
+ }
46
+ ```
47
+
48
+ ## Basic Type Safety
49
+
50
+ Type your Fastify application:
51
+
52
+ ```typescript
53
+ import Fastify, { type FastifyInstance, type FastifyRequest, type FastifyReply } from 'fastify';
54
+
55
+ const app: FastifyInstance = Fastify({ logger: true });
56
+
57
+ app.get('/health', async (request: FastifyRequest, reply: FastifyReply) => {
58
+ return { status: 'ok' };
59
+ });
60
+
61
+ await app.listen({ port: 3000 });
62
+ ```
63
+
64
+ ## Typing Route Handlers
65
+
66
+ Use generics to type request parts:
67
+
68
+ ```typescript
69
+ import type { FastifyRequest, FastifyReply } from 'fastify';
70
+
71
+ interface CreateUserBody {
72
+ name: string;
73
+ email: string;
74
+ }
75
+
76
+ interface UserParams {
77
+ id: string;
78
+ }
79
+
80
+ interface UserQuery {
81
+ include?: string;
82
+ }
83
+
84
+ // Type the request with generics
85
+ app.post<{
86
+ Body: CreateUserBody;
87
+ }>('/users', async (request, reply) => {
88
+ const { name, email } = request.body; // Fully typed
89
+ return { name, email };
90
+ });
91
+
92
+ app.get<{
93
+ Params: UserParams;
94
+ Querystring: UserQuery;
95
+ }>('/users/:id', async (request) => {
96
+ const { id } = request.params; // string
97
+ const { include } = request.query; // string | undefined
98
+ return { id, include };
99
+ });
100
+
101
+ // Full route options typing
102
+ app.route<{
103
+ Params: UserParams;
104
+ Querystring: UserQuery;
105
+ Body: CreateUserBody;
106
+ Reply: { user: { id: string; name: string } };
107
+ }>({
108
+ method: 'PUT',
109
+ url: '/users/:id',
110
+ handler: async (request, reply) => {
111
+ return { user: { id: request.params.id, name: request.body.name } };
112
+ },
113
+ });
114
+ ```
115
+
116
+ ## Type Providers
117
+
118
+ Use @fastify/type-provider-typebox for runtime + compile-time safety:
119
+
120
+ ```typescript
121
+ import Fastify from 'fastify';
122
+ import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
123
+ import { Type } from '@sinclair/typebox';
124
+
125
+ const app = Fastify().withTypeProvider<TypeBoxTypeProvider>();
126
+
127
+ const UserSchema = Type.Object({
128
+ id: Type.String(),
129
+ name: Type.String(),
130
+ email: Type.String({ format: 'email' }),
131
+ });
132
+
133
+ const CreateUserSchema = Type.Object({
134
+ name: Type.String({ minLength: 1 }),
135
+ email: Type.String({ format: 'email' }),
136
+ });
137
+
138
+ app.post('/users', {
139
+ schema: {
140
+ body: CreateUserSchema,
141
+ response: {
142
+ 201: UserSchema,
143
+ },
144
+ },
145
+ }, async (request, reply) => {
146
+ // request.body is typed as { name: string; email: string }
147
+ const { name, email } = request.body;
148
+
149
+ reply.code(201);
150
+ return { id: 'generated', name, email };
151
+ });
152
+ ```
153
+
154
+ ## Typing Decorators
155
+
156
+ Extend Fastify types with declaration merging:
157
+
158
+ ```typescript
159
+ import Fastify from 'fastify';
160
+
161
+ // Declare types for decorators
162
+ declare module 'fastify' {
163
+ interface FastifyInstance {
164
+ config: {
165
+ port: number;
166
+ host: string;
167
+ };
168
+ db: Database;
169
+ }
170
+
171
+ interface FastifyRequest {
172
+ user?: {
173
+ id: string;
174
+ email: string;
175
+ role: string;
176
+ };
177
+ startTime: number;
178
+ }
179
+
180
+ interface FastifyReply {
181
+ sendSuccess: (data: unknown) => void;
182
+ }
183
+ }
184
+
185
+ const app = Fastify();
186
+
187
+ // Add decorators
188
+ app.decorate('config', { port: 3000, host: 'localhost' });
189
+ app.decorate('db', new Database());
190
+
191
+ app.decorateRequest('user', null);
192
+ app.decorateRequest('startTime', 0);
193
+
194
+ app.decorateReply('sendSuccess', function (data: unknown) {
195
+ this.send({ success: true, data });
196
+ });
197
+
198
+ // Now fully typed
199
+ app.get('/profile', async (request, reply) => {
200
+ const user = request.user; // { id: string; email: string; role: string } | undefined
201
+ const config = app.config; // { port: number; host: string }
202
+
203
+ reply.sendSuccess({ user });
204
+ });
205
+ ```
206
+
207
+ ## Typing Plugins
208
+
209
+ Type plugin options and exports:
210
+
211
+ ```typescript
212
+ import fp from 'fastify-plugin';
213
+ import type { FastifyPluginAsync } from 'fastify';
214
+
215
+ interface DatabasePluginOptions {
216
+ connectionString: string;
217
+ poolSize?: number;
218
+ }
219
+
220
+ declare module 'fastify' {
221
+ interface FastifyInstance {
222
+ db: {
223
+ query: (sql: string, params?: unknown[]) => Promise<unknown[]>;
224
+ close: () => Promise<void>;
225
+ };
226
+ }
227
+ }
228
+
229
+ const databasePlugin: FastifyPluginAsync<DatabasePluginOptions> = async (
230
+ fastify,
231
+ options,
232
+ ) => {
233
+ const { connectionString, poolSize = 10 } = options;
234
+
235
+ const db = await createConnection(connectionString, poolSize);
236
+
237
+ fastify.decorate('db', {
238
+ query: (sql: string, params?: unknown[]) => db.query(sql, params),
239
+ close: () => db.end(),
240
+ });
241
+
242
+ fastify.addHook('onClose', async () => {
243
+ await db.end();
244
+ });
245
+ };
246
+
247
+ export default fp(databasePlugin, {
248
+ name: 'database',
249
+ });
250
+ ```
251
+
252
+ ## Typing Hooks
253
+
254
+ Type hook functions:
255
+
256
+ ```typescript
257
+ import type {
258
+ FastifyRequest,
259
+ FastifyReply,
260
+ onRequestHookHandler,
261
+ preHandlerHookHandler,
262
+ } from 'fastify';
263
+
264
+ const authHook: preHandlerHookHandler = async (
265
+ request: FastifyRequest,
266
+ reply: FastifyReply,
267
+ ) => {
268
+ const token = request.headers.authorization;
269
+ if (!token) {
270
+ reply.code(401).send({ error: 'Unauthorized' });
271
+ return;
272
+ }
273
+ request.user = await verifyToken(token);
274
+ };
275
+
276
+ const timingHook: onRequestHookHandler = async (request) => {
277
+ request.startTime = Date.now();
278
+ };
279
+
280
+ app.addHook('onRequest', timingHook);
281
+ app.addHook('preHandler', authHook);
282
+ ```
283
+
284
+ ## Typing Schema Objects
285
+
286
+ Create reusable typed schemas:
287
+
288
+ ```typescript
289
+ import type { JSONSchema7 } from 'json-schema';
290
+
291
+ // Define schema with const assertion for type inference
292
+ const userSchema = {
293
+ type: 'object',
294
+ properties: {
295
+ id: { type: 'string' },
296
+ name: { type: 'string' },
297
+ email: { type: 'string', format: 'email' },
298
+ },
299
+ required: ['id', 'name', 'email'],
300
+ } as const satisfies JSONSchema7;
301
+
302
+ // Infer TypeScript type from schema
303
+ type User = {
304
+ id: string;
305
+ name: string;
306
+ email: string;
307
+ };
308
+
309
+ app.get<{ Reply: User }>('/users/:id', {
310
+ schema: {
311
+ response: {
312
+ 200: userSchema,
313
+ },
314
+ },
315
+ }, async (request) => {
316
+ return { id: '1', name: 'John', email: 'john@example.com' };
317
+ });
318
+ ```
319
+
320
+ ## Shared Types
321
+
322
+ Organize types in dedicated files:
323
+
324
+ ```typescript
325
+ // types/index.ts
326
+ export interface User {
327
+ id: string;
328
+ name: string;
329
+ email: string;
330
+ role: 'admin' | 'user';
331
+ }
332
+
333
+ export interface CreateUserInput {
334
+ name: string;
335
+ email: string;
336
+ }
337
+
338
+ export interface PaginationQuery {
339
+ page?: number;
340
+ limit?: number;
341
+ sort?: string;
342
+ }
343
+
344
+ // routes/users.ts
345
+ import type { FastifyInstance } from 'fastify';
346
+ import type { User, CreateUserInput, PaginationQuery } from '../types/index.js';
347
+
348
+ export default async function userRoutes(fastify: FastifyInstance) {
349
+ fastify.get<{
350
+ Querystring: PaginationQuery;
351
+ Reply: { users: User[]; total: number };
352
+ }>('/', async (request) => {
353
+ const { page = 1, limit = 10 } = request.query;
354
+ // ...
355
+ });
356
+
357
+ fastify.post<{
358
+ Body: CreateUserInput;
359
+ Reply: User;
360
+ }>('/', async (request, reply) => {
361
+ reply.code(201);
362
+ // ...
363
+ });
364
+ }
365
+ ```
366
+
367
+ ## Type-Safe Route Registration
368
+
369
+ Create typed route factories:
370
+
371
+ ```typescript
372
+ import type { FastifyInstance, RouteOptions } from 'fastify';
373
+
374
+ function createCrudRoutes<T extends { id: string }>(
375
+ fastify: FastifyInstance,
376
+ options: {
377
+ prefix: string;
378
+ schema: {
379
+ item: object;
380
+ create: object;
381
+ update: object;
382
+ };
383
+ handlers: {
384
+ list: () => Promise<T[]>;
385
+ get: (id: string) => Promise<T | null>;
386
+ create: (data: unknown) => Promise<T>;
387
+ update: (id: string, data: unknown) => Promise<T>;
388
+ delete: (id: string) => Promise<void>;
389
+ };
390
+ },
391
+ ) {
392
+ const { prefix, schema, handlers } = options;
393
+
394
+ fastify.get(`${prefix}`, {
395
+ schema: { response: { 200: { type: 'array', items: schema.item } } },
396
+ }, async () => handlers.list());
397
+
398
+ fastify.get(`${prefix}/:id`, {
399
+ schema: { response: { 200: schema.item } },
400
+ }, async (request) => {
401
+ const item = await handlers.get((request.params as { id: string }).id);
402
+ if (!item) throw { statusCode: 404, message: 'Not found' };
403
+ return item;
404
+ });
405
+
406
+ // ... more routes
407
+ }
408
+ ```
409
+
410
+ ## Avoiding Type Gymnastics
411
+
412
+ Keep types simple and practical:
413
+
414
+ ```typescript
415
+ // GOOD - simple, readable types
416
+ interface UserRequest {
417
+ Params: { id: string };
418
+ Body: { name: string };
419
+ }
420
+
421
+ app.put<UserRequest>('/users/:id', handler);
422
+
423
+ // AVOID - overly complex generic types
424
+ type DeepPartial<T> = T extends object ? {
425
+ [P in keyof T]?: DeepPartial<T[P]>;
426
+ } : T;
427
+
428
+ // AVOID - excessive type inference
429
+ type InferSchemaType<T> = T extends { properties: infer P }
430
+ ? { [K in keyof P]: InferPropertyType<P[K]> }
431
+ : never;
432
+ ```
433
+
434
+ ## Type Checking Without Compilation
435
+
436
+ Use TypeScript for type checking only:
437
+
438
+ ```bash
439
+ # Type check without emitting
440
+ npx tsc --noEmit
441
+
442
+ # Watch mode
443
+ npx tsc --noEmit --watch
444
+
445
+ # In CI
446
+ npm run typecheck
447
+ ```
448
+
449
+ ```json
450
+ // package.json
451
+ {
452
+ "scripts": {
453
+ "start": "node app.ts",
454
+ "typecheck": "tsc --noEmit",
455
+ "test": "npm run typecheck && node --test"
456
+ }
457
+ }
458
+ ```