create-fluxstack 1.9.1 → 1.12.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/.dockerignore +1 -2
- package/Dockerfile +8 -8
- package/LIVE_COMPONENTS_REVIEW.md +781 -0
- package/LLMD/INDEX.md +64 -0
- package/LLMD/MAINTENANCE.md +197 -0
- package/LLMD/MIGRATION.md +156 -0
- package/LLMD/config/.gitkeep +1 -0
- package/LLMD/config/declarative-system.md +268 -0
- package/LLMD/config/environment-vars.md +327 -0
- package/LLMD/config/runtime-reload.md +401 -0
- package/LLMD/core/.gitkeep +1 -0
- package/LLMD/core/build-system.md +599 -0
- package/LLMD/core/framework-lifecycle.md +229 -0
- package/LLMD/core/plugin-system.md +451 -0
- package/LLMD/patterns/.gitkeep +1 -0
- package/LLMD/patterns/anti-patterns.md +297 -0
- package/LLMD/patterns/project-structure.md +264 -0
- package/LLMD/patterns/type-safety.md +440 -0
- package/LLMD/reference/.gitkeep +1 -0
- package/LLMD/reference/cli-commands.md +250 -0
- package/LLMD/reference/plugin-hooks.md +357 -0
- package/LLMD/reference/routing.md +39 -0
- package/LLMD/reference/troubleshooting.md +364 -0
- package/LLMD/resources/.gitkeep +1 -0
- package/LLMD/resources/controllers.md +465 -0
- package/LLMD/resources/live-components.md +703 -0
- package/LLMD/resources/live-rooms.md +482 -0
- package/LLMD/resources/live-upload.md +130 -0
- package/LLMD/resources/plugins-external.md +617 -0
- package/LLMD/resources/routes-eden.md +254 -0
- package/README.md +37 -17
- package/app/client/index.html +0 -1
- package/app/client/src/App.tsx +109 -156
- package/app/client/src/components/AppLayout.tsx +68 -0
- package/app/client/src/components/BackButton.tsx +13 -0
- package/app/client/src/components/DemoPage.tsx +20 -0
- package/app/client/src/components/LiveUploadWidget.tsx +204 -0
- package/app/client/src/lib/eden-api.ts +85 -65
- package/app/client/src/live/ChatDemo.tsx +107 -0
- package/app/client/src/live/CounterDemo.tsx +206 -0
- package/app/client/src/live/FormDemo.tsx +119 -0
- package/app/client/src/live/RoomChatDemo.tsx +242 -0
- package/app/client/src/live/UploadDemo.tsx +21 -0
- package/app/client/src/main.tsx +13 -10
- package/app/client/src/pages/ApiTestPage.tsx +108 -0
- package/app/client/src/pages/HomePage.tsx +76 -0
- package/app/client/src/vite-env.d.ts +1 -1
- package/app/server/app.ts +1 -4
- package/app/server/controllers/users.controller.ts +36 -44
- package/app/server/index.ts +24 -107
- package/app/server/live/LiveChat.ts +77 -0
- package/app/server/live/LiveCounter.ts +67 -0
- package/app/server/live/LiveForm.ts +63 -0
- package/app/server/live/LiveLocalCounter.ts +32 -0
- package/app/server/live/LiveRoomChat.ts +285 -0
- package/app/server/live/LiveUpload.ts +81 -0
- package/app/server/live/register-components.ts +19 -19
- package/app/server/routes/index.ts +3 -1
- package/app/server/routes/room.routes.ts +117 -0
- package/app/server/routes/users.routes.ts +35 -27
- package/app/shared/types/index.ts +14 -2
- package/config/app.config.ts +2 -62
- package/config/client.config.ts +2 -95
- package/config/database.config.ts +2 -99
- package/config/fluxstack.config.ts +25 -45
- package/config/index.ts +57 -38
- package/config/monitoring.config.ts +2 -114
- package/config/plugins.config.ts +2 -80
- package/config/server.config.ts +2 -68
- package/config/services.config.ts +2 -130
- package/config/system/app.config.ts +29 -0
- package/config/system/build.config.ts +49 -0
- package/config/system/client.config.ts +68 -0
- package/config/system/database.config.ts +17 -0
- package/config/system/fluxstack.config.ts +114 -0
- package/config/{logger.config.ts → system/logger.config.ts} +3 -1
- package/config/system/monitoring.config.ts +114 -0
- package/config/system/plugins.config.ts +84 -0
- package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
- package/config/system/server.config.ts +68 -0
- package/config/system/services.config.ts +46 -0
- package/config/{system.config.ts → system/system.config.ts} +1 -1
- package/core/build/bundler.ts +4 -1
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +159 -27
- package/core/build/live-components-generator.ts +70 -3
- package/core/build/optimizer.ts +235 -235
- package/core/cli/command-registry.ts +6 -4
- package/core/cli/commands/build.ts +79 -0
- package/core/cli/commands/create.ts +54 -0
- package/core/cli/commands/dev.ts +101 -0
- package/core/cli/commands/help.ts +34 -0
- package/core/cli/commands/index.ts +34 -0
- package/core/cli/commands/make-plugin.ts +90 -0
- package/core/cli/commands/plugin-add.ts +197 -0
- package/core/cli/commands/plugin-deps.ts +2 -2
- package/core/cli/commands/plugin-list.ts +208 -0
- package/core/cli/commands/plugin-remove.ts +170 -0
- package/core/cli/generators/component.ts +769 -769
- package/core/cli/generators/controller.ts +1 -1
- package/core/cli/generators/index.ts +146 -146
- package/core/cli/generators/interactive.ts +227 -227
- package/core/cli/generators/plugin.ts +2 -2
- package/core/cli/generators/prompts.ts +82 -82
- package/core/cli/generators/route.ts +6 -6
- package/core/cli/generators/service.ts +2 -2
- package/core/cli/generators/template-engine.ts +4 -3
- package/core/cli/generators/types.ts +2 -2
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +115 -558
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/LiveComponentsProvider.tsx +63 -17
- package/core/client/api/eden.ts +183 -0
- package/core/client/api/index.ts +11 -0
- package/core/client/components/Live.tsx +104 -0
- package/core/client/fluxstack.ts +1 -9
- package/core/client/hooks/AdaptiveChunkSizer.ts +215 -0
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/hooks/useChunkedUpload.ts +170 -69
- package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
- package/core/client/hooks/useLiveComponent.ts +800 -0
- package/core/client/hooks/useLiveUpload.ts +71 -0
- package/core/client/hooks/useRoom.ts +409 -0
- package/core/client/hooks/useRoomProxy.ts +382 -0
- package/core/client/index.ts +18 -51
- package/core/client/standalone-entry.ts +8 -0
- package/core/client/standalone.ts +74 -53
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +70 -291
- package/core/config/schema.ts +42 -723
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +227 -47
- package/core/framework/types.ts +2 -2
- package/core/index.ts +23 -4
- package/core/live/ComponentRegistry.ts +7 -3
- package/core/live/types.ts +77 -0
- package/core/plugins/built-in/index.ts +134 -131
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1074
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +111 -47
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +68 -265
- package/core/plugins/built-in/vite/index.ts +94 -306
- package/core/plugins/built-in/vite/vite-dev.ts +82 -0
- package/core/plugins/config.ts +9 -7
- package/core/plugins/dependency-manager.ts +31 -1
- package/core/plugins/discovery.ts +19 -7
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +203 -203
- package/core/plugins/manager.ts +27 -39
- package/core/plugins/module-resolver.ts +19 -8
- package/core/plugins/registry.ts +309 -21
- package/core/plugins/types.ts +106 -55
- package/core/server/framework.ts +66 -43
- package/core/server/index.ts +15 -16
- package/core/server/live/ComponentRegistry.ts +91 -75
- package/core/server/live/FileUploadManager.ts +41 -31
- package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
- package/core/server/live/LiveRoomManager.ts +261 -0
- package/core/server/live/RoomEventBus.ts +234 -0
- package/core/server/live/RoomStateManager.ts +172 -0
- package/core/server/live/StateSignature.ts +643 -643
- package/core/server/live/WebSocketConnectionManager.ts +30 -19
- package/core/server/live/auto-generated-components.ts +41 -26
- package/core/server/live/index.ts +14 -0
- package/core/server/live/websocket-plugin.ts +233 -72
- package/core/server/middleware/elysia-helpers.ts +7 -2
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +180 -180
- package/core/server/plugins/static-files-plugin.ts +69 -260
- package/core/server/plugins/swagger.ts +33 -33
- package/core/server/rooms/RoomBroadcaster.ts +357 -0
- package/core/server/rooms/RoomSystem.ts +463 -0
- package/core/server/rooms/index.ts +13 -0
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +12 -12
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/build.ts +219 -218
- package/core/types/config.ts +56 -26
- package/core/types/index.ts +4 -4
- package/core/types/plugin.ts +107 -99
- package/core/types/types.ts +490 -14
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +2 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +36 -1
- package/core/utils/errors/index.ts +49 -5
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +6 -16
- package/core/utils/index.ts +17 -17
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +13 -9
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +6 -1
- package/core/utils/logger/stack-trace.ts +3 -1
- package/core/utils/logger/startup-banner.ts +82 -66
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +66 -66
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +8 -7
- package/eslint.config.js +23 -23
- package/package.json +14 -15
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
- package/plugins/crypto-auth/client/components/index.ts +11 -11
- package/plugins/crypto-auth/client/index.ts +11 -11
- package/plugins/crypto-auth/config/index.ts +1 -1
- package/plugins/crypto-auth/index.ts +4 -4
- package/plugins/crypto-auth/package.json +65 -65
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/index.ts +21 -21
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/plugins/crypto-auth/server/middlewares.ts +19 -19
- package/tsconfig.api-strict.json +16 -0
- package/tsconfig.json +10 -14
- package/{app/client/tsconfig.node.json → tsconfig.node.json} +1 -1
- package/types/global.d.ts +29 -29
- package/types/vitest.d.ts +8 -8
- package/vite.config.ts +38 -62
- package/vitest.config.live.ts +10 -9
- package/vitest.config.ts +29 -17
- package/workspace.json +5 -5
- package/app/client/README.md +0 -69
- package/app/client/SIMPLIFICATION.md +0 -140
- package/app/client/frontend-only.ts +0 -12
- package/app/client/tsconfig.app.json +0 -44
- package/app/client/tsconfig.json +0 -7
- package/app/client/zustand-setup.md +0 -65
- package/app/server/backend-only.ts +0 -18
- package/app/server/live/LiveClockComponent.ts +0 -215
- package/app/server/routes/env-test.ts +0 -110
- package/core/client/hooks/index.ts +0 -7
- package/core/client/hooks/useHybridLiveComponent.ts +0 -631
- package/core/client/hooks/useWebSocket.ts +0 -373
- package/core/config/env.ts +0 -546
- package/core/config/loader.ts +0 -522
- package/core/config/runtime-config.ts +0 -327
- package/core/config/validator.ts +0 -540
- package/core/server/backend-entry.ts +0 -51
- package/core/server/standalone.ts +0 -106
- package/core/utils/regenerate-files.ts +0 -69
- package/fluxstack.config.ts +0 -354
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Framework Lifecycle
|
|
2
|
+
|
|
3
|
+
**Version:** 1.11.0 | **Updated:** 2025-02-08
|
|
4
|
+
|
|
5
|
+
## Quick Facts
|
|
6
|
+
|
|
7
|
+
- Framework class: `FluxStackFramework` in `core/framework/server.ts`
|
|
8
|
+
- Initialization: Constructor → Plugin Discovery → Setup Hooks → Server Start
|
|
9
|
+
- Request flow: 13 hook points from request to response
|
|
10
|
+
- Shutdown: Graceful with reverse-order plugin cleanup
|
|
11
|
+
- Plugin loading: Dependency-based topological sort with priority
|
|
12
|
+
|
|
13
|
+
## Initialization Sequence
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
sequenceDiagram
|
|
17
|
+
participant App as Application
|
|
18
|
+
participant FW as FluxStackFramework
|
|
19
|
+
participant PM as PluginManager
|
|
20
|
+
participant PR as PluginRegistry
|
|
21
|
+
participant Plugins as Plugins
|
|
22
|
+
|
|
23
|
+
App->>FW: new FluxStackFramework(config)
|
|
24
|
+
FW->>FW: Create Elysia app
|
|
25
|
+
FW->>PR: new PluginRegistry()
|
|
26
|
+
FW->>PM: new PluginManager()
|
|
27
|
+
FW->>FW: setupCors()
|
|
28
|
+
FW->>FW: setupHeadHandler()
|
|
29
|
+
FW->>FW: setupHooks()
|
|
30
|
+
FW->>FW: setupErrorHandling()
|
|
31
|
+
FW->>PM: initializeAutomaticPlugins()
|
|
32
|
+
PM->>PM: discoverPlugins()
|
|
33
|
+
PM->>PR: register(plugin)
|
|
34
|
+
PR->>PR: updateLoadOrder()
|
|
35
|
+
loop For each plugin
|
|
36
|
+
PM->>Plugins: onConfigLoad(context)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
App->>FW: start()
|
|
40
|
+
FW->>PR: validateDependencies()
|
|
41
|
+
loop For each plugin (load order)
|
|
42
|
+
FW->>Plugins: setup(context)
|
|
43
|
+
end
|
|
44
|
+
loop For each plugin (load order)
|
|
45
|
+
FW->>Plugins: onBeforeServerStart(context)
|
|
46
|
+
end
|
|
47
|
+
loop For each plugin (load order)
|
|
48
|
+
FW->>Plugins: Mount plugin routes
|
|
49
|
+
end
|
|
50
|
+
loop For each plugin (load order)
|
|
51
|
+
FW->>Plugins: onServerStart(context)
|
|
52
|
+
end
|
|
53
|
+
loop For each plugin (load order)
|
|
54
|
+
FW->>Plugins: onAfterServerStart(context)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
App->>FW: listen(port)
|
|
58
|
+
FW->>FW: Display startup banner
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Plugin Loading Order
|
|
62
|
+
|
|
63
|
+
Plugins are loaded in dependency-aware order:
|
|
64
|
+
|
|
65
|
+
1. **Discovery Phase** (automatic, during constructor):
|
|
66
|
+
- Scan `plugins/` directory (project plugins)
|
|
67
|
+
- Scan `node_modules/` for npm plugins (if enabled)
|
|
68
|
+
- Whitelist validation for npm plugins
|
|
69
|
+
- Dependency resolution
|
|
70
|
+
|
|
71
|
+
2. **Registration Phase**:
|
|
72
|
+
- Validate plugin structure
|
|
73
|
+
- Store in registry
|
|
74
|
+
- Build dependency graph
|
|
75
|
+
- Calculate load order (topological sort)
|
|
76
|
+
|
|
77
|
+
3. **Configuration Phase** (during `initializeAutomaticPlugins()`):
|
|
78
|
+
- Execute `onConfigLoad` hooks in load order
|
|
79
|
+
- Plugins can modify configuration
|
|
80
|
+
|
|
81
|
+
4. **Setup Phase** (during `start()`):
|
|
82
|
+
- Validate all dependencies exist
|
|
83
|
+
- Execute `setup` hooks in load order
|
|
84
|
+
- Execute `onBeforeServerStart` hooks
|
|
85
|
+
- Mount plugin routes (if plugin has Elysia plugin)
|
|
86
|
+
- Execute `onServerStart` hooks
|
|
87
|
+
- Execute `onAfterServerStart` hooks
|
|
88
|
+
|
|
89
|
+
## Hook Execution Order
|
|
90
|
+
|
|
91
|
+
### Lifecycle Hooks
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
onConfigLoad → setup → onBeforeServerStart → onServerStart → onAfterServerStart
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- **onConfigLoad**: Modify configuration before framework starts
|
|
98
|
+
- **setup**: Initialize plugin resources (databases, connections)
|
|
99
|
+
- **onBeforeServerStart**: Register routes, middleware
|
|
100
|
+
- **onServerStart**: Start background tasks
|
|
101
|
+
- **onAfterServerStart**: Post-startup tasks (logging, metrics)
|
|
102
|
+
|
|
103
|
+
### Request/Response Pipeline
|
|
104
|
+
|
|
105
|
+
```mermaid
|
|
106
|
+
graph TD
|
|
107
|
+
A[Incoming Request] --> B[onRequest]
|
|
108
|
+
B --> C[onRequestValidation]
|
|
109
|
+
C --> D{Validation Failed?}
|
|
110
|
+
D -->|Yes| E[Return 400 Error]
|
|
111
|
+
D -->|No| F[onBeforeRoute]
|
|
112
|
+
F --> G{Plugin Handled?}
|
|
113
|
+
G -->|Yes| H[Return Plugin Response]
|
|
114
|
+
G -->|No| I[Route Handler]
|
|
115
|
+
I --> J[onAfterRoute]
|
|
116
|
+
J --> K[onBeforeResponse]
|
|
117
|
+
K --> L[onResponseTransform]
|
|
118
|
+
L --> M[Log Request]
|
|
119
|
+
M --> N[onResponse]
|
|
120
|
+
N --> O[Return Response]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Hook Execution Order:**
|
|
124
|
+
|
|
125
|
+
1. **onRequest**: Log request, authenticate, add context
|
|
126
|
+
2. **onRequestValidation**: Custom validation logic
|
|
127
|
+
3. **onBeforeRoute**: Handle request before routing (auth, caching)
|
|
128
|
+
4. **[Route Handler Executes]**
|
|
129
|
+
5. **onAfterRoute**: Access route params, log matched route
|
|
130
|
+
6. **onBeforeResponse**: Modify headers, status code
|
|
131
|
+
7. **onResponseTransform**: Transform response body
|
|
132
|
+
8. **[Automatic Request Logging]**
|
|
133
|
+
9. **onResponse**: Final logging, metrics collection
|
|
134
|
+
|
|
135
|
+
### Error Handling Flow
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Error Occurs → onError (each plugin) → Plugin Handled? → Return Response or Default Error
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- Plugins can handle errors by setting `context.handled = true`
|
|
142
|
+
- Vite plugin uses this for SPA fallback
|
|
143
|
+
- FluxStackError instances use custom status codes
|
|
144
|
+
- Unhandled errors return 500 with message (dev) or generic (prod)
|
|
145
|
+
|
|
146
|
+
## Request Lifecycle Details
|
|
147
|
+
|
|
148
|
+
### CORS Setup
|
|
149
|
+
|
|
150
|
+
Applied via `onRequest` hook:
|
|
151
|
+
- Sets `Access-Control-Allow-Origin`
|
|
152
|
+
- Sets `Access-Control-Allow-Methods`
|
|
153
|
+
- Sets `Access-Control-Allow-Headers`
|
|
154
|
+
- Handles OPTIONS preflight requests
|
|
155
|
+
|
|
156
|
+
### HEAD Request Handling
|
|
157
|
+
|
|
158
|
+
Global HEAD handler prevents Elysia bug:
|
|
159
|
+
- Returns empty body with appropriate headers
|
|
160
|
+
- API routes: `Content-Type: application/json`
|
|
161
|
+
- Static files: `Content-Type: text/html` or appropriate type
|
|
162
|
+
|
|
163
|
+
### Request Timing
|
|
164
|
+
|
|
165
|
+
- Start time stored in `onRequest`
|
|
166
|
+
- Duration calculated in `onAfterHandle`
|
|
167
|
+
- Timing key stored in response headers
|
|
168
|
+
- Cleanup after response sent
|
|
169
|
+
|
|
170
|
+
## Shutdown Sequence
|
|
171
|
+
|
|
172
|
+
```mermaid
|
|
173
|
+
sequenceDiagram
|
|
174
|
+
participant Signal as SIGTERM/SIGINT
|
|
175
|
+
participant FW as FluxStackFramework
|
|
176
|
+
participant Plugins as Plugins
|
|
177
|
+
|
|
178
|
+
Signal->>FW: Shutdown signal
|
|
179
|
+
FW->>FW: stop()
|
|
180
|
+
loop For each plugin (reverse order)
|
|
181
|
+
FW->>Plugins: onBeforeServerStop(context)
|
|
182
|
+
end
|
|
183
|
+
loop For each plugin (reverse order)
|
|
184
|
+
FW->>Plugins: onServerStop(context)
|
|
185
|
+
end
|
|
186
|
+
FW->>FW: Set isStarted = false
|
|
187
|
+
FW->>Signal: process.exit(0)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Shutdown Hooks:**
|
|
191
|
+
|
|
192
|
+
1. **onBeforeServerStop**: Prepare for shutdown (stop accepting requests)
|
|
193
|
+
2. **onServerStop**: Cleanup resources (close connections, save state)
|
|
194
|
+
|
|
195
|
+
**Reverse Order**: Plugins shut down in reverse of load order to respect dependencies
|
|
196
|
+
|
|
197
|
+
## Plugin Context
|
|
198
|
+
|
|
199
|
+
Every plugin receives a `PluginContext` object:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
{
|
|
203
|
+
config: FluxStackConfig, // Full framework configuration
|
|
204
|
+
logger: Logger, // Plugin-specific logger
|
|
205
|
+
app: Elysia, // Elysia app instance
|
|
206
|
+
utils: PluginUtils, // Utility functions
|
|
207
|
+
registry: PluginRegistry // Access to other plugins
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Error Recovery
|
|
212
|
+
|
|
213
|
+
- Plugin hook failures are caught and logged
|
|
214
|
+
- `onPluginError` hook notified on all other plugins
|
|
215
|
+
- Framework continues execution (non-blocking)
|
|
216
|
+
- Build hooks can stop build on error
|
|
217
|
+
|
|
218
|
+
## Performance Considerations
|
|
219
|
+
|
|
220
|
+
- Plugin discovery is asynchronous (non-blocking)
|
|
221
|
+
- Hooks execute sequentially (predictable order)
|
|
222
|
+
- Request timing tracked with minimal overhead
|
|
223
|
+
- Automatic cleanup of timing data
|
|
224
|
+
|
|
225
|
+
## Related
|
|
226
|
+
|
|
227
|
+
- [Plugin System](./plugin-system.md) - Plugin architecture details
|
|
228
|
+
- [Plugin Hooks Reference](../reference/plugin-hooks.md) - Complete hook list
|
|
229
|
+
- [Build System](./build-system.md) - Build lifecycle
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
# Plugin System
|
|
2
|
+
|
|
3
|
+
**Version:** 1.11.0 | **Updated:** 2025-02-08
|
|
4
|
+
|
|
5
|
+
## Quick Facts
|
|
6
|
+
|
|
7
|
+
- Plugin interface: `FluxStack.Plugin` in `core/plugins/types.ts`
|
|
8
|
+
- Registry: `PluginRegistry` manages all plugins
|
|
9
|
+
- Manager: `PluginManager` handles lifecycle and execution
|
|
10
|
+
- Discovery: Automatic from `plugins/` and `node_modules/`
|
|
11
|
+
- Security: Whitelist system for npm plugins
|
|
12
|
+
- Dependencies: Automatic resolution with topological sort
|
|
13
|
+
|
|
14
|
+
## Plugin Interface
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
interface FluxStack.Plugin {
|
|
18
|
+
// Required
|
|
19
|
+
name: string
|
|
20
|
+
|
|
21
|
+
// Optional metadata
|
|
22
|
+
version?: string
|
|
23
|
+
description?: string
|
|
24
|
+
author?: string
|
|
25
|
+
dependencies?: string[] // Plugin dependencies
|
|
26
|
+
priority?: number | PluginPriority
|
|
27
|
+
category?: string
|
|
28
|
+
tags?: string[]
|
|
29
|
+
|
|
30
|
+
// Lifecycle hooks (20+ available)
|
|
31
|
+
setup?: (context: PluginContext) => void | Promise<void>
|
|
32
|
+
onConfigLoad?: (context: ConfigLoadContext) => void | Promise<void>
|
|
33
|
+
onBeforeServerStart?: (context: PluginContext) => void | Promise<void>
|
|
34
|
+
onServerStart?: (context: PluginContext) => void | Promise<void>
|
|
35
|
+
onAfterServerStart?: (context: PluginContext) => void | Promise<void>
|
|
36
|
+
onBeforeServerStop?: (context: PluginContext) => void | Promise<void>
|
|
37
|
+
onServerStop?: (context: PluginContext) => void | Promise<void>
|
|
38
|
+
|
|
39
|
+
// Request/Response hooks
|
|
40
|
+
onRequest?: (context: RequestContext) => void | Promise<void>
|
|
41
|
+
onBeforeRoute?: (context: RequestContext) => void | Promise<void>
|
|
42
|
+
onAfterRoute?: (context: RouteContext) => void | Promise<void>
|
|
43
|
+
onBeforeResponse?: (context: ResponseContext) => void | Promise<void>
|
|
44
|
+
onResponse?: (context: ResponseContext) => void | Promise<void>
|
|
45
|
+
onRequestValidation?: (context: ValidationContext) => void | Promise<void>
|
|
46
|
+
onResponseTransform?: (context: TransformContext) => void | Promise<void>
|
|
47
|
+
|
|
48
|
+
// Error handling
|
|
49
|
+
onError?: (context: ErrorContext) => void | Promise<void>
|
|
50
|
+
|
|
51
|
+
// Build hooks
|
|
52
|
+
onBeforeBuild?: (context: BuildContext) => void | Promise<void>
|
|
53
|
+
onBuild?: (context: BuildContext) => void | Promise<void>
|
|
54
|
+
onBuildAsset?: (context: BuildAssetContext) => void | Promise<void>
|
|
55
|
+
onBuildComplete?: (context: BuildContext) => void | Promise<void>
|
|
56
|
+
onBuildError?: (context: BuildErrorContext) => void | Promise<void>
|
|
57
|
+
|
|
58
|
+
// Plugin system hooks
|
|
59
|
+
onPluginRegister?: (context: PluginEventContext) => void | Promise<void>
|
|
60
|
+
onPluginUnregister?: (context: PluginEventContext) => void | Promise<void>
|
|
61
|
+
onPluginError?: (context: PluginEventContext & { error: Error }) => void | Promise<void>
|
|
62
|
+
|
|
63
|
+
// CLI commands
|
|
64
|
+
commands?: CliCommand[]
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Plugin Discovery
|
|
69
|
+
|
|
70
|
+
### Built-in vs External Plugins
|
|
71
|
+
|
|
72
|
+
**Built-in Plugins** (`core/plugins/built-in/`):
|
|
73
|
+
- Part of framework core
|
|
74
|
+
- Manually registered via `.use(plugin)`
|
|
75
|
+
- Examples: vite, swagger, static, live-components
|
|
76
|
+
- No automatic discovery (developer chooses)
|
|
77
|
+
|
|
78
|
+
**Project Plugins** (`plugins/`):
|
|
79
|
+
- User-created plugins in project
|
|
80
|
+
- Automatically discovered if `PLUGINS_DISCOVER_PROJECT=true`
|
|
81
|
+
- Always trusted (no whitelist required)
|
|
82
|
+
- Can have local dependencies
|
|
83
|
+
|
|
84
|
+
**NPM Plugins** (`node_modules/`):
|
|
85
|
+
- Third-party plugins from npm
|
|
86
|
+
- Automatically discovered if `PLUGINS_DISCOVER_NPM=true`
|
|
87
|
+
- **Requires whitelist** (`PLUGINS_ALLOWED` env var)
|
|
88
|
+
- Naming patterns:
|
|
89
|
+
- `fluxstack-plugin-*`
|
|
90
|
+
- `fplugin-*`
|
|
91
|
+
- `@fluxstack/plugin-*`
|
|
92
|
+
- `@fplugin/*`
|
|
93
|
+
- `@org/fluxstack-plugin-*`
|
|
94
|
+
- `@org/fplugin-*`
|
|
95
|
+
|
|
96
|
+
### Discovery Process
|
|
97
|
+
|
|
98
|
+
```mermaid
|
|
99
|
+
graph TD
|
|
100
|
+
A[Plugin Discovery] --> B{PLUGINS_DISCOVER_PROJECT?}
|
|
101
|
+
B -->|Yes| C[Scan plugins/ directory]
|
|
102
|
+
B -->|No| D[Skip project plugins]
|
|
103
|
+
C --> E[Load plugin files]
|
|
104
|
+
|
|
105
|
+
A --> F{PLUGINS_DISCOVER_NPM?}
|
|
106
|
+
F -->|Yes| G[Scan node_modules/]
|
|
107
|
+
F -->|No| H[Skip npm plugins]
|
|
108
|
+
G --> I{In PLUGINS_ALLOWED?}
|
|
109
|
+
I -->|Yes| J[Load npm plugin]
|
|
110
|
+
I -->|No| K[Block plugin]
|
|
111
|
+
|
|
112
|
+
E --> L[Register in PluginRegistry]
|
|
113
|
+
J --> L
|
|
114
|
+
L --> M[Resolve dependencies]
|
|
115
|
+
M --> N[Calculate load order]
|
|
116
|
+
N --> O[Execute onConfigLoad hooks]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Plugin Security
|
|
120
|
+
|
|
121
|
+
### Whitelist System
|
|
122
|
+
|
|
123
|
+
**Purpose**: Prevent supply chain attacks from malicious npm packages
|
|
124
|
+
|
|
125
|
+
**Configuration**:
|
|
126
|
+
```bash
|
|
127
|
+
# Enable npm plugin discovery
|
|
128
|
+
PLUGINS_DISCOVER_NPM=true
|
|
129
|
+
|
|
130
|
+
# Whitelist specific plugins
|
|
131
|
+
PLUGINS_ALLOWED=fluxstack-plugin-auth,@acme/fplugin-payments
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Security Model**:
|
|
135
|
+
- Project plugins (`plugins/`) are **always trusted** (developer added them)
|
|
136
|
+
- NPM plugins (`node_modules/`) **require whitelist** (supply chain protection)
|
|
137
|
+
- Empty whitelist = no npm plugins allowed
|
|
138
|
+
- Blocked plugins logged with warning
|
|
139
|
+
|
|
140
|
+
**Example**:
|
|
141
|
+
```typescript
|
|
142
|
+
// ✅ Project plugin - always allowed
|
|
143
|
+
plugins/my-plugin/index.ts
|
|
144
|
+
|
|
145
|
+
// ❌ NPM plugin without whitelist - blocked
|
|
146
|
+
node_modules/fluxstack-plugin-malicious/
|
|
147
|
+
|
|
148
|
+
// ✅ NPM plugin in whitelist - allowed
|
|
149
|
+
node_modules/fluxstack-plugin-auth/ // if in PLUGINS_ALLOWED
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Dependency Resolution
|
|
153
|
+
|
|
154
|
+
### Dependency Declaration
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
export default {
|
|
158
|
+
name: 'my-plugin',
|
|
159
|
+
dependencies: ['database', 'auth'], // Requires these plugins
|
|
160
|
+
setup: async (context) => {
|
|
161
|
+
// Can safely use database and auth plugins
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Load Order Algorithm
|
|
167
|
+
|
|
168
|
+
1. **Build Dependency Graph**: Map all plugin dependencies
|
|
169
|
+
2. **Topological Sort**: Order plugins so dependencies load first
|
|
170
|
+
3. **Circular Detection**: Throw error if circular dependency found
|
|
171
|
+
4. **Priority Sort**: Within dependency groups, sort by priority
|
|
172
|
+
|
|
173
|
+
**Priority Values**:
|
|
174
|
+
- `highest` or `100+`: Load first (core infrastructure)
|
|
175
|
+
- `high` or `50-99`: Load early (auth, database)
|
|
176
|
+
- `normal` or `0-49`: Default (most plugins)
|
|
177
|
+
- `low` or `-50 to -1`: Load late (monitoring)
|
|
178
|
+
- `lowest` or `-100 or less`: Load last (cleanup)
|
|
179
|
+
|
|
180
|
+
**Example Load Order**:
|
|
181
|
+
```
|
|
182
|
+
database (priority: 100, no deps)
|
|
183
|
+
↓
|
|
184
|
+
auth (priority: 50, deps: [database])
|
|
185
|
+
↓
|
|
186
|
+
api (priority: 0, deps: [auth])
|
|
187
|
+
↓
|
|
188
|
+
monitoring (priority: -50, deps: [api])
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Plugin Registry
|
|
192
|
+
|
|
193
|
+
### PluginRegistry Class
|
|
194
|
+
|
|
195
|
+
**Responsibilities**:
|
|
196
|
+
- Store all registered plugins
|
|
197
|
+
- Manage plugin manifests
|
|
198
|
+
- Calculate load order
|
|
199
|
+
- Validate dependencies
|
|
200
|
+
- Discover plugins from filesystem
|
|
201
|
+
|
|
202
|
+
**Key Methods**:
|
|
203
|
+
```typescript
|
|
204
|
+
register(plugin: Plugin, manifest?: PluginManifest): Promise<void>
|
|
205
|
+
unregister(name: string): Promise<void>
|
|
206
|
+
get(name: string): Plugin | undefined
|
|
207
|
+
getAll(): Plugin[]
|
|
208
|
+
getLoadOrder(): string[]
|
|
209
|
+
getDependencies(pluginName: string): string[]
|
|
210
|
+
getDependents(pluginName: string): string[]
|
|
211
|
+
has(name: string): boolean
|
|
212
|
+
discoverPlugins(options: PluginDiscoveryOptions): Promise<PluginLoadResult[]>
|
|
213
|
+
discoverNpmPlugins(): Promise<PluginLoadResult[]>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Plugin Manifest
|
|
217
|
+
|
|
218
|
+
Optional `plugin.json` or `package.json` with `fluxstack` field:
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"name": "fluxstack-plugin-auth",
|
|
223
|
+
"version": "1.0.0",
|
|
224
|
+
"description": "Authentication plugin",
|
|
225
|
+
"author": "Your Name",
|
|
226
|
+
"license": "MIT",
|
|
227
|
+
"dependencies": {
|
|
228
|
+
"jsonwebtoken": "^9.0.0"
|
|
229
|
+
},
|
|
230
|
+
"fluxstack": {
|
|
231
|
+
"version": "1.11.0",
|
|
232
|
+
"hooks": ["setup", "onRequest", "onBeforeRoute"],
|
|
233
|
+
"category": "security",
|
|
234
|
+
"tags": ["auth", "jwt"]
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Plugin Manager
|
|
240
|
+
|
|
241
|
+
### PluginManager Class
|
|
242
|
+
|
|
243
|
+
**Responsibilities**:
|
|
244
|
+
- Initialize plugin system
|
|
245
|
+
- Execute plugin hooks
|
|
246
|
+
- Manage plugin contexts
|
|
247
|
+
- Track plugin metrics
|
|
248
|
+
- Handle hook errors
|
|
249
|
+
|
|
250
|
+
**Key Methods**:
|
|
251
|
+
```typescript
|
|
252
|
+
initialize(): Promise<void>
|
|
253
|
+
shutdown(): Promise<void>
|
|
254
|
+
registerPlugin(plugin: Plugin): Promise<void>
|
|
255
|
+
unregisterPlugin(name: string): void
|
|
256
|
+
executeHook(hook: PluginHook, context?: any, options?: HookExecutionOptions): Promise<PluginHookResult[]>
|
|
257
|
+
executePluginHook(plugin: Plugin, hook: PluginHook, context?: any): Promise<PluginHookResult>
|
|
258
|
+
getPluginMetrics(pluginName?: string): PluginMetrics | Map<string, PluginMetrics>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Hook Execution
|
|
262
|
+
|
|
263
|
+
**Sequential Execution** (default):
|
|
264
|
+
```typescript
|
|
265
|
+
await pluginManager.executeHook('onRequest', requestContext)
|
|
266
|
+
// Plugins execute in load order, one at a time
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Parallel Execution**:
|
|
270
|
+
```typescript
|
|
271
|
+
await pluginManager.executeHook('onBuild', buildContext, { parallel: true })
|
|
272
|
+
// All plugins execute simultaneously
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Options**:
|
|
276
|
+
- `timeout`: Max execution time (default: 30s)
|
|
277
|
+
- `parallel`: Execute all plugins at once
|
|
278
|
+
- `stopOnError`: Stop if any plugin fails
|
|
279
|
+
- `retries`: Retry failed hooks (default: 0)
|
|
280
|
+
|
|
281
|
+
### Plugin Metrics
|
|
282
|
+
|
|
283
|
+
Tracked per plugin:
|
|
284
|
+
```typescript
|
|
285
|
+
{
|
|
286
|
+
loadTime: number, // Time to load plugin
|
|
287
|
+
setupTime: number, // Time to execute setup hook
|
|
288
|
+
hookExecutions: Map<PluginHook, number>, // Count per hook
|
|
289
|
+
errors: number, // Total errors
|
|
290
|
+
warnings: number, // Total warnings
|
|
291
|
+
lastExecution?: Date // Last hook execution time
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Plugin Context
|
|
296
|
+
|
|
297
|
+
Every hook receives appropriate context:
|
|
298
|
+
|
|
299
|
+
### PluginContext (Lifecycle Hooks)
|
|
300
|
+
```typescript
|
|
301
|
+
{
|
|
302
|
+
config: FluxStackConfig, // Full configuration
|
|
303
|
+
logger: Logger, // Plugin-specific logger
|
|
304
|
+
app: Elysia, // Elysia app instance
|
|
305
|
+
utils: PluginUtils, // Utility functions
|
|
306
|
+
registry: PluginRegistry // Access other plugins
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### RequestContext (Request Hooks)
|
|
311
|
+
```typescript
|
|
312
|
+
{
|
|
313
|
+
request: Request,
|
|
314
|
+
path: string,
|
|
315
|
+
method: string,
|
|
316
|
+
headers: Record<string, string>,
|
|
317
|
+
query: Record<string, string>,
|
|
318
|
+
params: Record<string, string>,
|
|
319
|
+
body?: any,
|
|
320
|
+
user?: any,
|
|
321
|
+
startTime: number,
|
|
322
|
+
handled?: boolean, // Set to true to handle request
|
|
323
|
+
response?: Response // Set to return custom response
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### ResponseContext (Response Hooks)
|
|
328
|
+
```typescript
|
|
329
|
+
{
|
|
330
|
+
...RequestContext,
|
|
331
|
+
response: Response,
|
|
332
|
+
statusCode: number,
|
|
333
|
+
duration: number,
|
|
334
|
+
size?: number
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### ErrorContext (Error Hooks)
|
|
339
|
+
```typescript
|
|
340
|
+
{
|
|
341
|
+
...RequestContext,
|
|
342
|
+
error: Error,
|
|
343
|
+
duration: number,
|
|
344
|
+
handled: boolean // Set to true to handle error
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Plugin Utilities
|
|
349
|
+
|
|
350
|
+
Available in `context.utils`:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
{
|
|
354
|
+
createTimer: (label: string) => { end: () => number },
|
|
355
|
+
formatBytes: (bytes: number) => string,
|
|
356
|
+
isProduction: () => boolean,
|
|
357
|
+
isDevelopment: () => boolean,
|
|
358
|
+
getEnvironment: () => string,
|
|
359
|
+
createHash: (data: string) => string,
|
|
360
|
+
deepMerge: (target: any, source: any) => any,
|
|
361
|
+
validateSchema: (data: any, schema: any) => { valid: boolean; errors: string[] }
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Error Handling
|
|
366
|
+
|
|
367
|
+
### Hook Failures
|
|
368
|
+
|
|
369
|
+
- Caught and logged automatically
|
|
370
|
+
- Other plugins notified via `onPluginError` hook
|
|
371
|
+
- Framework continues execution (non-blocking)
|
|
372
|
+
- Metrics updated with error count
|
|
373
|
+
|
|
374
|
+
### Plugin Errors
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
onPluginError: async (context) => {
|
|
378
|
+
// context.pluginName - which plugin failed
|
|
379
|
+
// context.error - the error that occurred
|
|
380
|
+
// context.timestamp - when it happened
|
|
381
|
+
|
|
382
|
+
// Log to monitoring service
|
|
383
|
+
await monitoring.logPluginError(context)
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Dependency Management
|
|
388
|
+
|
|
389
|
+
### Automatic Installation
|
|
390
|
+
|
|
391
|
+
**Project Plugins**:
|
|
392
|
+
- Dependencies installed in plugin directory
|
|
393
|
+
- Runs `bun install` in plugin folder
|
|
394
|
+
- Isolated from main project dependencies
|
|
395
|
+
|
|
396
|
+
**NPM Plugins**:
|
|
397
|
+
- Dependencies must be manually reviewed
|
|
398
|
+
- Run `bun run flux plugin:deps install <plugin-name>`
|
|
399
|
+
- Security: prevents automatic malicious package installation
|
|
400
|
+
|
|
401
|
+
### Missing Dependencies
|
|
402
|
+
|
|
403
|
+
If plugin declares dependencies not in main `package.json`:
|
|
404
|
+
- Project plugins: Auto-install locally
|
|
405
|
+
- NPM plugins: Show warning with install command
|
|
406
|
+
|
|
407
|
+
## Plugin Validation
|
|
408
|
+
|
|
409
|
+
### Structure Validation
|
|
410
|
+
|
|
411
|
+
Required:
|
|
412
|
+
- `name` property (string)
|
|
413
|
+
|
|
414
|
+
Optional but validated:
|
|
415
|
+
- `version` (string)
|
|
416
|
+
- `dependencies` (array of strings)
|
|
417
|
+
- `priority` (number)
|
|
418
|
+
|
|
419
|
+
### Configuration Validation
|
|
420
|
+
|
|
421
|
+
If plugin has `configSchema`, validates config against schema:
|
|
422
|
+
```typescript
|
|
423
|
+
{
|
|
424
|
+
configSchema: {
|
|
425
|
+
type: 'object',
|
|
426
|
+
properties: {
|
|
427
|
+
apiKey: { type: 'string' },
|
|
428
|
+
timeout: { type: 'number' }
|
|
429
|
+
},
|
|
430
|
+
required: ['apiKey']
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
## Best Practices
|
|
436
|
+
|
|
437
|
+
1. **Declare Dependencies**: Always list plugin dependencies
|
|
438
|
+
2. **Use Priority**: Set priority for load order control
|
|
439
|
+
3. **Handle Errors**: Implement error hooks for resilience
|
|
440
|
+
4. **Cleanup Resources**: Use `onServerStop` for cleanup
|
|
441
|
+
5. **Avoid Blocking**: Keep hooks fast, use async for I/O
|
|
442
|
+
6. **Log Appropriately**: Use `context.logger` for plugin logs
|
|
443
|
+
7. **Validate Input**: Check context data before use
|
|
444
|
+
8. **Test Isolation**: Ensure plugin works independently
|
|
445
|
+
|
|
446
|
+
## Related
|
|
447
|
+
|
|
448
|
+
- [Framework Lifecycle](./framework-lifecycle.md) - How plugins integrate
|
|
449
|
+
- [Plugin Hooks Reference](../reference/plugin-hooks.md) - Complete hook list
|
|
450
|
+
- [External Plugins](../resources/plugins-external.md) - Creating plugins
|
|
451
|
+
- [CLI Commands](../reference/cli-commands.md) - Plugin management commands
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Patterns documentation directory
|