@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,320 @@
1
+ ---
2
+ name: plugins
3
+ description: Plugin development and encapsulation in Fastify
4
+ metadata:
5
+ tags: plugins, encapsulation, modules, architecture
6
+ ---
7
+
8
+ # Plugin Development and Encapsulation
9
+
10
+ ## Understanding Encapsulation
11
+
12
+ Fastify's plugin system provides automatic encapsulation. Each plugin creates its own context, isolating decorators, hooks, and plugins registered within it:
13
+
14
+ ```typescript
15
+ import Fastify from 'fastify';
16
+ import fp from 'fastify-plugin';
17
+
18
+ const app = Fastify();
19
+
20
+ // This plugin is encapsulated - its decorators are NOT available to siblings
21
+ app.register(async function childPlugin(fastify) {
22
+ fastify.decorate('privateUtil', () => 'only available here');
23
+
24
+ // This decorator is only available within this plugin and its children
25
+ fastify.get('/child', async function (request, reply) {
26
+ return this.privateUtil();
27
+ });
28
+ });
29
+
30
+ // This route CANNOT access privateUtil - it's in a different context
31
+ app.get('/parent', async function (request, reply) {
32
+ // this.privateUtil is undefined here
33
+ return { status: 'ok' };
34
+ });
35
+ ```
36
+
37
+ ## Breaking Encapsulation with fastify-plugin
38
+
39
+ Use `fastify-plugin` when you need to share decorators, hooks, or plugins with the parent context:
40
+
41
+ ```typescript
42
+ import fp from 'fastify-plugin';
43
+
44
+ // This plugin's decorators will be available to the parent and siblings
45
+ export default fp(async function databasePlugin(fastify, options) {
46
+ const db = await createConnection(options.connectionString);
47
+
48
+ fastify.decorate('db', db);
49
+
50
+ fastify.addHook('onClose', async () => {
51
+ await db.close();
52
+ });
53
+ }, {
54
+ name: 'database-plugin',
55
+ dependencies: [], // List plugin dependencies
56
+ });
57
+ ```
58
+
59
+ ## Plugin Registration Order
60
+
61
+ Plugins are registered in order, but loading is asynchronous. Use `after()` for sequential dependencies:
62
+
63
+ ```typescript
64
+ import Fastify from 'fastify';
65
+ import databasePlugin from './plugins/database.js';
66
+ import authPlugin from './plugins/auth.js';
67
+ import routesPlugin from './routes/index.js';
68
+
69
+ const app = Fastify();
70
+
71
+ // Database must be ready before auth
72
+ app.register(databasePlugin);
73
+
74
+ // Auth depends on database
75
+ app.register(authPlugin);
76
+
77
+ // Routes depend on both
78
+ app.register(routesPlugin);
79
+
80
+ // Or use after() for explicit sequencing
81
+ app.register(databasePlugin).after(() => {
82
+ app.register(authPlugin).after(() => {
83
+ app.register(routesPlugin);
84
+ });
85
+ });
86
+
87
+ await app.ready();
88
+ ```
89
+
90
+ ## Plugin Options
91
+
92
+ Always validate and document plugin options:
93
+
94
+ ```typescript
95
+ import fp from 'fastify-plugin';
96
+
97
+ interface CachePluginOptions {
98
+ ttl: number;
99
+ maxSize?: number;
100
+ prefix?: string;
101
+ }
102
+
103
+ export default fp<CachePluginOptions>(async function cachePlugin(fastify, options) {
104
+ const { ttl, maxSize = 1000, prefix = 'cache:' } = options;
105
+
106
+ if (typeof ttl !== 'number' || ttl <= 0) {
107
+ throw new Error('Cache plugin requires a positive ttl option');
108
+ }
109
+
110
+ const cache = new Map<string, { value: unknown; expires: number }>();
111
+
112
+ fastify.decorate('cache', {
113
+ get(key: string): unknown | undefined {
114
+ const item = cache.get(prefix + key);
115
+ if (!item) return undefined;
116
+ if (Date.now() > item.expires) {
117
+ cache.delete(prefix + key);
118
+ return undefined;
119
+ }
120
+ return item.value;
121
+ },
122
+ set(key: string, value: unknown): void {
123
+ if (cache.size >= maxSize) {
124
+ const firstKey = cache.keys().next().value;
125
+ cache.delete(firstKey);
126
+ }
127
+ cache.set(prefix + key, { value, expires: Date.now() + ttl });
128
+ },
129
+ });
130
+ }, {
131
+ name: 'cache-plugin',
132
+ });
133
+ ```
134
+
135
+ ## Plugin Factory Pattern
136
+
137
+ Create configurable plugins using factory functions:
138
+
139
+ ```typescript
140
+ import fp from 'fastify-plugin';
141
+
142
+ interface RateLimitOptions {
143
+ max: number;
144
+ timeWindow: number;
145
+ }
146
+
147
+ function createRateLimiter(defaults: Partial<RateLimitOptions> = {}) {
148
+ return fp<RateLimitOptions>(async function rateLimitPlugin(fastify, options) {
149
+ const config = { ...defaults, ...options };
150
+
151
+ // Implementation
152
+ fastify.decorate('rateLimit', config);
153
+ }, {
154
+ name: 'rate-limiter',
155
+ });
156
+ }
157
+
158
+ // Usage
159
+ app.register(createRateLimiter({ max: 100 }), { timeWindow: 60000 });
160
+ ```
161
+
162
+ ## Plugin Dependencies
163
+
164
+ Declare dependencies to ensure proper load order:
165
+
166
+ ```typescript
167
+ import fp from 'fastify-plugin';
168
+
169
+ export default fp(async function authPlugin(fastify) {
170
+ // This plugin requires 'database-plugin' to be loaded first
171
+ if (!fastify.hasDecorator('db')) {
172
+ throw new Error('Auth plugin requires database plugin');
173
+ }
174
+
175
+ fastify.decorate('authenticate', async (request) => {
176
+ const user = await fastify.db.users.findByToken(request.headers.authorization);
177
+ return user;
178
+ });
179
+ }, {
180
+ name: 'auth-plugin',
181
+ dependencies: ['database-plugin'],
182
+ });
183
+ ```
184
+
185
+ ## Scoped Plugins for Route Groups
186
+
187
+ Use encapsulation to scope plugins to specific routes:
188
+
189
+ ```typescript
190
+ import Fastify from 'fastify';
191
+
192
+ const app = Fastify();
193
+
194
+ // Public routes - no auth required
195
+ app.register(async function publicRoutes(fastify) {
196
+ fastify.get('/health', async () => ({ status: 'ok' }));
197
+ fastify.get('/docs', async () => ({ version: '1.0.0' }));
198
+ });
199
+
200
+ // Protected routes - auth required
201
+ app.register(async function protectedRoutes(fastify) {
202
+ // Auth hook only applies to routes in this plugin
203
+ fastify.addHook('onRequest', async (request, reply) => {
204
+ const token = request.headers.authorization;
205
+ if (!token) {
206
+ reply.code(401).send({ error: 'Unauthorized' });
207
+ return;
208
+ }
209
+ request.user = await verifyToken(token);
210
+ });
211
+
212
+ fastify.get('/profile', async (request) => {
213
+ return { user: request.user };
214
+ });
215
+
216
+ fastify.get('/settings', async (request) => {
217
+ return { settings: await getSettings(request.user.id) };
218
+ });
219
+ });
220
+ ```
221
+
222
+ ## Prefix Routes with Register
223
+
224
+ Use the `prefix` option to namespace routes:
225
+
226
+ ```typescript
227
+ app.register(import('./routes/users.js'), { prefix: '/api/v1/users' });
228
+ app.register(import('./routes/posts.js'), { prefix: '/api/v1/posts' });
229
+
230
+ // In routes/users.js
231
+ export default async function userRoutes(fastify) {
232
+ // Becomes /api/v1/users
233
+ fastify.get('/', async () => {
234
+ return { users: [] };
235
+ });
236
+
237
+ // Becomes /api/v1/users/:id
238
+ fastify.get('/:id', async (request) => {
239
+ return { user: { id: request.params.id } };
240
+ });
241
+ }
242
+ ```
243
+
244
+ ## Plugin Metadata
245
+
246
+ Add metadata for documentation and tooling:
247
+
248
+ ```typescript
249
+ import fp from 'fastify-plugin';
250
+
251
+ async function metricsPlugin(fastify) {
252
+ // Implementation
253
+ }
254
+
255
+ export default fp(metricsPlugin, {
256
+ name: 'metrics-plugin',
257
+ fastify: '5.x', // Fastify version compatibility
258
+ dependencies: ['pino-plugin'],
259
+ decorators: {
260
+ fastify: ['db'], // Required decorators
261
+ request: [],
262
+ reply: [],
263
+ },
264
+ });
265
+ ```
266
+
267
+ ## Autoload Plugins
268
+
269
+ Use `@fastify/autoload` for automatic plugin loading:
270
+
271
+ ```typescript
272
+ import Fastify from 'fastify';
273
+ import autoload from '@fastify/autoload';
274
+ import { fileURLToPath } from 'node:url';
275
+ import { dirname, join } from 'node:path';
276
+
277
+ const __dirname = dirname(fileURLToPath(import.meta.url));
278
+
279
+ const app = Fastify();
280
+
281
+ // Load all plugins from the plugins directory
282
+ app.register(autoload, {
283
+ dir: join(__dirname, 'plugins'),
284
+ options: { prefix: '/api' },
285
+ });
286
+
287
+ // Load all routes from the routes directory
288
+ app.register(autoload, {
289
+ dir: join(__dirname, 'routes'),
290
+ options: { prefix: '/api' },
291
+ });
292
+ ```
293
+
294
+ ## Testing Plugins in Isolation
295
+
296
+ Test plugins independently:
297
+
298
+ ```typescript
299
+ import { describe, it, before, after } from 'node:test';
300
+ import Fastify from 'fastify';
301
+ import myPlugin from './my-plugin.js';
302
+
303
+ describe('MyPlugin', () => {
304
+ let app;
305
+
306
+ before(async () => {
307
+ app = Fastify();
308
+ app.register(myPlugin, { option: 'value' });
309
+ await app.ready();
310
+ });
311
+
312
+ after(async () => {
313
+ await app.close();
314
+ });
315
+
316
+ it('should decorate fastify instance', (t) => {
317
+ t.assert.ok(app.hasDecorator('myDecorator'));
318
+ });
319
+ });
320
+ ```