create-lego-one 2.0.12 → 2.0.14
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/dist/index.cjs +150 -15
- package/dist/index.cjs.map +1 -1
- package/package.json +1 -1
- package/template/.cursor/rules/rules.mdc +639 -0
- package/template/.dockerignore +58 -0
- package/template/.env.example +18 -0
- package/template/.eslintignore +5 -0
- package/template/.eslintrc.js +28 -0
- package/template/.prettierignore +6 -0
- package/template/.prettierrc +11 -0
- package/template/CLAUDE.md +634 -0
- package/template/Dockerfile +67 -0
- package/template/PROMPT.md +457 -0
- package/template/README.md +325 -0
- package/template/docker-compose.yml +48 -0
- package/template/docker-entrypoint.sh +23 -0
- package/template/docs/checkpoints/.template.md +64 -0
- package/template/docs/checkpoints/framework/01-infrastructure-setup.md +132 -0
- package/template/docs/checkpoints/framework/02-pocketbase-setup.md +155 -0
- package/template/docs/checkpoints/framework/03-host-kernel.md +170 -0
- package/template/docs/checkpoints/framework/04-auth-system.md +163 -0
- package/template/docs/checkpoints/framework/phase-05-multitenancy-rbac.md +223 -0
- package/template/docs/checkpoints/framework/phase-06-ui-components.md +260 -0
- package/template/docs/checkpoints/framework/phase-07-communication-system.md +276 -0
- package/template/docs/checkpoints/framework/phase-08-plugin-system.md +91 -0
- package/template/docs/checkpoints/framework/phase-09-dashboard-plugin.md +111 -0
- package/template/docs/checkpoints/framework/phase-10-todo-plugin.md +169 -0
- package/template/docs/checkpoints/framework/phase-11-testing.md +264 -0
- package/template/docs/checkpoints/framework/phase-12-deployment.md +294 -0
- package/template/docs/checkpoints/framework/phase-13-documentation.md +312 -0
- package/template/docs/framework/plans/00-index.md +164 -0
- package/template/docs/framework/plans/01-infrastructure-setup.md +855 -0
- package/template/docs/framework/plans/02-pocketbase-setup.md +1374 -0
- package/template/docs/framework/plans/03-host-kernel.md +1518 -0
- package/template/docs/framework/plans/04-auth-system.md +1466 -0
- package/template/docs/framework/plans/05-multitenancy-rbac.md +1527 -0
- package/template/docs/framework/plans/06-ui-components.md +1478 -0
- package/template/docs/framework/plans/07-communication-system.md +1106 -0
- package/template/docs/framework/plans/08-plugin-system.md +1179 -0
- package/template/docs/framework/plans/09-dashboard-plugin.md +1137 -0
- package/template/docs/framework/plans/10-todo-plugin.md +1343 -0
- package/template/docs/framework/plans/11-testing.md +935 -0
- package/template/docs/framework/plans/12-deployment.md +896 -0
- package/template/docs/framework/prompts/0-boilerplate-modernjs.md +151 -0
- package/template/docs/framework/research/00-modernjs-audit.md +488 -0
- package/template/docs/framework/research/01-system-blueprint.md +721 -0
- package/template/docs/framework/research/02-data-migration-protocol.md +699 -0
- package/template/docs/framework/research/03-host-setup.md +714 -0
- package/template/docs/framework/research/04-plugin-architecture.md +645 -0
- package/template/docs/framework/research/05-slot-injection-pattern.md +671 -0
- package/template/docs/framework/research/06-cli-strategy.md +615 -0
- package/template/docs/framework/research/07-deployment.md +629 -0
- package/template/docs/framework/research/README.md +282 -0
- package/template/docs/framework/setup/00-index.md +210 -0
- package/template/docs/framework/setup/01-framework-structure.md +308 -0
- package/template/docs/framework/setup/02-development-workflow.md +405 -0
- package/template/docs/framework/setup/03-environment-setup.md +215 -0
- package/template/docs/framework/setup/04-kernel-architecture.md +499 -0
- package/template/docs/framework/setup/05-plugin-system.md +620 -0
- package/template/docs/framework/setup/06-communication-patterns.md +451 -0
- package/template/docs/framework/setup/07-plugin-development.md +582 -0
- package/template/docs/framework/setup/08-component-library.md +658 -0
- package/template/docs/framework/setup/09-data-integration.md +609 -0
- package/template/docs/framework/setup/10-auth-rbac.md +497 -0
- package/template/docs/framework/setup/11-hooks-api.md +393 -0
- package/template/docs/framework/setup/12-components-api.md +665 -0
- package/template/docs/framework/setup/13-deployment-guide.md +566 -0
- package/template/docs/framework/setup/README.md +548 -0
- package/template/host/package.json +1 -1
- package/template/nginx.conf +72 -0
- package/template/package.json +1 -1
- package/template/packages/plugins/@lego/plugin-dashboard/package.json +1 -1
- package/template/packages/plugins/@lego/plugin-todo/package.json +1 -1
- package/template/pocketbase/CHANGELOG.md +911 -0
- package/template/pocketbase/LICENSE.md +17 -0
- package/template/scripts/create-plugin.js +221 -0
- package/template/scripts/deploy.sh +56 -0
- package/template/tsconfig.base.json +26 -0
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
# Plugin System
|
|
2
|
+
|
|
3
|
+
**Plugin Architecture, Loading, and Slot Injection**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The Lego-One plugin system enables modular feature development through Garfish micro-frontends. Plugins register slots, inject UI, and communicate with the host via channels.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Plugin Architecture
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
17
|
+
│ HOST KERNEL │
|
|
18
|
+
│ ┌────────────────────────────────────────────────────────────────┐ │
|
|
19
|
+
│ │ PLUGIN MANAGER │ │
|
|
20
|
+
│ │ • Plugin Loader (dynamic loading) │ │
|
|
21
|
+
│ │ • Plugin Store (state management) │ │
|
|
22
|
+
│ │ • Slot Registry (UI injection) │ │
|
|
23
|
+
│ └────────────────────────────────────────────────────────────────┘ │
|
|
24
|
+
│ │ │
|
|
25
|
+
│ ═══════════════════════════════ │
|
|
26
|
+
│ GARFISH PLUGIN BOUNDARY │
|
|
27
|
+
│ ═══════════════════════════════ │
|
|
28
|
+
│ │ │
|
|
29
|
+
│ ┌──────────────┬──────────────┬──────────────┬──────────────┐ │
|
|
30
|
+
│ │ Plugin 1 │ Plugin 2 │ Plugin 3 │ Plugin N │ │
|
|
31
|
+
│ │ Dashboard │ Todo │ Custom │ Future │ │
|
|
32
|
+
│ ├──────────────┼──────────────┼──────────────┼──────────────┤ │
|
|
33
|
+
│ │ plugin.config│ plugin.config│ plugin.config│ plugin.config│ │
|
|
34
|
+
│ │ Slot: nav │ Slot: nav │ Slot: widgets│ Slot: ??? │ │
|
|
35
|
+
│ │ Route: /dash │ Route: /todos│ Route: /custom│ Route: ??? │ │
|
|
36
|
+
│ └──────────────┴──────────────┴──────────────┴──────────────┘ │
|
|
37
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Plugin Structure
|
|
43
|
+
|
|
44
|
+
### Standard Plugin File Layout
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
packages/plugins/@lego/plugin-<name>/
|
|
48
|
+
├── package.json # Plugin package configuration
|
|
49
|
+
├── tsconfig.json # TypeScript configuration
|
|
50
|
+
├── modern.config.ts # Modern.js + Garfish config
|
|
51
|
+
├── tailwind.config.ts # Tailwind CSS configuration
|
|
52
|
+
├── postcss.config.mjs # PostCSS configuration
|
|
53
|
+
│
|
|
54
|
+
└── src/
|
|
55
|
+
├── global.css # Global plugin styles
|
|
56
|
+
├── plugin.config.ts # Plugin manifest & configuration
|
|
57
|
+
├── plugin.ts # Garfish entry point
|
|
58
|
+
├── App.tsx # Root component with routing
|
|
59
|
+
├── types.ts # TypeScript types
|
|
60
|
+
├── schemas.ts # Zod validation schemas
|
|
61
|
+
│
|
|
62
|
+
├── pages/ # Page components
|
|
63
|
+
│ └── <Plugin>Page.tsx
|
|
64
|
+
│
|
|
65
|
+
├── components/ # React components
|
|
66
|
+
│ └── slots/ # Slot injection components
|
|
67
|
+
│ └── SidebarWidget.tsx
|
|
68
|
+
│
|
|
69
|
+
├── hooks/ # Custom React hooks
|
|
70
|
+
│ ├── usePocketBase.ts
|
|
71
|
+
│ └── use<Feature>Data.ts
|
|
72
|
+
│
|
|
73
|
+
└── vite-env.d.ts # Vite type declarations
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Plugin Configuration
|
|
79
|
+
|
|
80
|
+
### plugin.config.ts
|
|
81
|
+
|
|
82
|
+
**Location:** `packages/plugins/@lego/plugin-<name>/src/plugin.config.ts`
|
|
83
|
+
|
|
84
|
+
This file defines the plugin's metadata, slots, routes, and settings.
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import type { PluginConfig } from '@lego/kernel/plugins';
|
|
88
|
+
import { SidebarWidget } from './components/slots/SidebarWidget';
|
|
89
|
+
import { DashboardPage } from './pages/DashboardPage';
|
|
90
|
+
|
|
91
|
+
export const pluginConfig: PluginConfig = {
|
|
92
|
+
// ==================== MANIFEST ====================
|
|
93
|
+
manifest: {
|
|
94
|
+
name: '@lego/plugin-dashboard',
|
|
95
|
+
version: '1.0.0',
|
|
96
|
+
displayName: 'Dashboard',
|
|
97
|
+
description: 'Organization statistics and activity feed',
|
|
98
|
+
author: 'Lego-One',
|
|
99
|
+
permissions: ['dashboard.read'],
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
// ==================== ENABLED STATE ====================
|
|
103
|
+
enabled: true,
|
|
104
|
+
|
|
105
|
+
// ==================== SLOT INJECTIONS ====================
|
|
106
|
+
slots: [
|
|
107
|
+
{
|
|
108
|
+
slot: 'sidebar:nav',
|
|
109
|
+
component: () => import('./components/slots/SidebarWidget').then(m => m.default),
|
|
110
|
+
order: 100,
|
|
111
|
+
props: {},
|
|
112
|
+
condition: undefined,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
slot: 'dashboard:widgets',
|
|
116
|
+
component: () => import('./components/StatsCard').then(m => m.default),
|
|
117
|
+
order: 50,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
|
|
121
|
+
// ==================== ROUTES ====================
|
|
122
|
+
routes: [
|
|
123
|
+
{
|
|
124
|
+
path: '/dashboard',
|
|
125
|
+
component: () => import('./pages/DashboardPage').then(m => m.default),
|
|
126
|
+
protected: true,
|
|
127
|
+
permissions: ['dashboard.read'],
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
|
|
131
|
+
// ==================== SETTINGS ====================
|
|
132
|
+
settings: [
|
|
133
|
+
{
|
|
134
|
+
key: 'showActivityFeed',
|
|
135
|
+
type: 'boolean',
|
|
136
|
+
label: 'Show Activity Feed',
|
|
137
|
+
description: 'Display recent activity on dashboard',
|
|
138
|
+
defaultValue: true,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
key: 'refreshInterval',
|
|
142
|
+
type: 'number',
|
|
143
|
+
label: 'Refresh Interval (seconds)',
|
|
144
|
+
description: 'How often to refresh dashboard data',
|
|
145
|
+
defaultValue: 30,
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Slot System
|
|
154
|
+
|
|
155
|
+
### Available Slots
|
|
156
|
+
|
|
157
|
+
| Slot Name | Location | Purpose | Example Use |
|
|
158
|
+
|-----------|----------|---------|-------------|
|
|
159
|
+
| `sidebar:top` | Top of sidebar | Logo, branding | Plugin logo |
|
|
160
|
+
| `sidebar:nav` | Main navigation area | Navigation links | Plugin nav link |
|
|
161
|
+
| `sidebar:bottom` | Bottom of sidebar | Logout, settings | Settings link |
|
|
162
|
+
| `topbar:left` | Left side of header | Breadcrumbs, back button | Parent nav |
|
|
163
|
+
| `topbar:center` | Center of header | Page title, search | Search bar |
|
|
164
|
+
| `topbar:right` | Right side of header | Notifications, user menu | Notification icon |
|
|
165
|
+
| `dashboard:widgets` | Dashboard page | Statistics, activity | Stats cards |
|
|
166
|
+
| `settings:menu` | Settings page | Settings sections | Plugin settings |
|
|
167
|
+
|
|
168
|
+
### Slot Injection Example
|
|
169
|
+
|
|
170
|
+
**File:** `packages/plugins/@lego/plugin-dashboard/src/components/slots/SidebarWidget.tsx`
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { NavLink } from '@modern-js/runtime/router';
|
|
174
|
+
import { LayoutDashboard } from 'lucide-react';
|
|
175
|
+
|
|
176
|
+
export function SidebarWidget() {
|
|
177
|
+
return (
|
|
178
|
+
<NavLink
|
|
179
|
+
to="/dashboard"
|
|
180
|
+
className={({ isActive }) =>
|
|
181
|
+
`flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors ${
|
|
182
|
+
isActive
|
|
183
|
+
? 'bg-primary text-primary-foreground'
|
|
184
|
+
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
185
|
+
}`
|
|
186
|
+
}
|
|
187
|
+
>
|
|
188
|
+
<LayoutDashboard className="h-5 w-5" />
|
|
189
|
+
<span>Dashboard</span>
|
|
190
|
+
</NavLink>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export default SidebarWidget;
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Plugin Loading
|
|
200
|
+
|
|
201
|
+
### Dev Mode (Separate Servers)
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Each plugin runs independently
|
|
205
|
+
pnpm run dev:all
|
|
206
|
+
|
|
207
|
+
# Results in:
|
|
208
|
+
# Host: http://localhost:8080
|
|
209
|
+
# Dashboard: http://localhost:3001
|
|
210
|
+
# Todo: http://localhost:3002
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**modern.runtime.ts (dev entries):**
|
|
214
|
+
```typescript
|
|
215
|
+
{
|
|
216
|
+
name: '@lego/plugin-dashboard',
|
|
217
|
+
entry: 'http://localhost:3001', // Dev: separate server
|
|
218
|
+
activeWhen: '/dashboard',
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Production Mode (Bundled)
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
pnpm run build
|
|
226
|
+
|
|
227
|
+
# All plugins bundled into host/dist/
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**modern.runtime.ts (prod entries):**
|
|
231
|
+
```typescript
|
|
232
|
+
{
|
|
233
|
+
name: '@lego/plugin-dashboard',
|
|
234
|
+
entry: () => import('@lego/plugin-dashboard'), // Prod: dynamic import
|
|
235
|
+
activeWhen: '/dashboard',
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Plugin Enable/Disable
|
|
242
|
+
|
|
243
|
+
### Via Config File
|
|
244
|
+
|
|
245
|
+
**File:** `host/src/saas.config.ts`
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
export const saasConfig = {
|
|
249
|
+
plugins: [
|
|
250
|
+
{ name: '@lego/plugin-dashboard', enabled: true },
|
|
251
|
+
{ name: '@lego/plugin-todo', enabled: false }, // Disabled
|
|
252
|
+
],
|
|
253
|
+
};
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Via Plugin Store (Runtime)
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { usePluginStore } from '@lego/kernel/plugins';
|
|
260
|
+
|
|
261
|
+
function PluginManager() {
|
|
262
|
+
const { plugins, enablePlugin, disablePlugin } = usePluginStore();
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<div>
|
|
266
|
+
{Object.values(plugins).map((plugin) => (
|
|
267
|
+
<div key={plugin.id}>
|
|
268
|
+
<span>{plugin.manifest.displayName}</span>
|
|
269
|
+
<button
|
|
270
|
+
onClick={() => plugin.enabled
|
|
271
|
+
? disablePlugin(plugin.id)
|
|
272
|
+
: enablePlugin(plugin.id)
|
|
273
|
+
}
|
|
274
|
+
>
|
|
275
|
+
{plugin.enabled ? 'Disable' : 'Enable'}
|
|
276
|
+
</button>
|
|
277
|
+
</div>
|
|
278
|
+
))}
|
|
279
|
+
</div>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Plugin Routes
|
|
287
|
+
|
|
288
|
+
### Route Registration
|
|
289
|
+
|
|
290
|
+
Routes are registered in `plugin.config.ts` and automatically loaded by Garfish.
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
routes: [
|
|
294
|
+
{
|
|
295
|
+
path: '/my-plugin',
|
|
296
|
+
component: () => import('./pages/MyPluginPage').then(m => m.default),
|
|
297
|
+
protected: true, // Requires authentication
|
|
298
|
+
permissions: ['plugin.read'], // Requires specific permission
|
|
299
|
+
},
|
|
300
|
+
]
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Protected Routes
|
|
304
|
+
|
|
305
|
+
Routes with `protected: true` will redirect unauthenticated users to sign-in.
|
|
306
|
+
|
|
307
|
+
### Permission-Gated Routes
|
|
308
|
+
|
|
309
|
+
Routes with `permissions` require the user to have the specified permissions.
|
|
310
|
+
|
|
311
|
+
**Host handles this automatically** - plugins just declare requirements.
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Plugin Communication
|
|
316
|
+
|
|
317
|
+
### Using Channel Bus
|
|
318
|
+
|
|
319
|
+
**File:** `packages/plugins/@lego/plugin-todo/src/hooks/useToast.ts`
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
export function useNotifySuccess() {
|
|
323
|
+
return (title: string, description?: string) => {
|
|
324
|
+
// Publish to channel bus
|
|
325
|
+
window.__LEGO_CHANNEL_BUS__?.publish('lego:toast', {
|
|
326
|
+
type: 'success',
|
|
327
|
+
title,
|
|
328
|
+
description,
|
|
329
|
+
});
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Usage
|
|
334
|
+
const notifySuccess = useNotifySuccess();
|
|
335
|
+
notifySuccess('Todo created', 'Your todo was created successfully');
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Reading Kernel State
|
|
339
|
+
|
|
340
|
+
**File:** `packages/plugins/@lego/plugin-todo/src/hooks/useOrganization.ts`
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
export function useCurrentOrganization() {
|
|
344
|
+
const kernelState = window.__LEGO_KERNEL_STATE__?.useGlobalKernelState?.getState();
|
|
345
|
+
|
|
346
|
+
return kernelState?.organization || null;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Usage
|
|
350
|
+
const organization = useCurrentOrganization();
|
|
351
|
+
const orgId = organization?.id;
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Subscribing to Events
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
useEffect(() => {
|
|
358
|
+
const channelBus = window.__LEGO_CHANNEL_BUS__;
|
|
359
|
+
if (!channelBus) return;
|
|
360
|
+
|
|
361
|
+
// Listen for auth changes
|
|
362
|
+
const unsubscribe = channelBus.subscribe('lego:auth:change', (data) => {
|
|
363
|
+
console.log('Auth changed:', data);
|
|
364
|
+
if (data.isAuthenticated) {
|
|
365
|
+
// Refresh data
|
|
366
|
+
} else {
|
|
367
|
+
// Clear data
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
return unsubscribe;
|
|
372
|
+
}, []);
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Plugin Settings
|
|
378
|
+
|
|
379
|
+
### Defining Settings
|
|
380
|
+
|
|
381
|
+
**In plugin.config.ts:**
|
|
382
|
+
```typescript
|
|
383
|
+
settings: [
|
|
384
|
+
{
|
|
385
|
+
key: 'featureEnabled',
|
|
386
|
+
type: 'boolean',
|
|
387
|
+
label: 'Enable Feature',
|
|
388
|
+
defaultValue: true,
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
key: 'apiKey',
|
|
392
|
+
type: 'string',
|
|
393
|
+
label: 'API Key',
|
|
394
|
+
description: 'External service API key',
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
key: 'refreshRate',
|
|
398
|
+
type: 'select',
|
|
399
|
+
label: 'Refresh Rate',
|
|
400
|
+
options: [
|
|
401
|
+
{ label: 'Slow (60s)', value: '60' },
|
|
402
|
+
{ label: 'Fast (10s)', value: '10' },
|
|
403
|
+
],
|
|
404
|
+
defaultValue: '30',
|
|
405
|
+
},
|
|
406
|
+
]
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Reading Settings
|
|
410
|
+
|
|
411
|
+
Settings are stored in the plugin store and can be accessed via the store.
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
import { usePluginStore } from '@lego/kernel/plugins';
|
|
415
|
+
|
|
416
|
+
function MyPluginComponent() {
|
|
417
|
+
const pluginState = usePluginStore((state) => state.plugins['@lego/plugin-my-plugin']);
|
|
418
|
+
|
|
419
|
+
const settings = pluginState?.settings || {};
|
|
420
|
+
|
|
421
|
+
return (
|
|
422
|
+
<div>
|
|
423
|
+
<p>Feature Enabled: {settings.featureEnabled}</p>
|
|
424
|
+
<p>Refresh Rate: {settings.refreshRate}s</p>
|
|
425
|
+
</div>
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## Creating a New Plugin
|
|
433
|
+
|
|
434
|
+
### Quick Method (CLI)
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
pnpm run create-plugin my-new-plugin
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Manual Method
|
|
441
|
+
|
|
442
|
+
1. **Create directory:**
|
|
443
|
+
```bash
|
|
444
|
+
mkdir -p packages/plugins/@lego/plugin-my-new-plugin
|
|
445
|
+
cd packages/plugins/@lego/plugin-my-new-plugin
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
2. **Create package.json**
|
|
449
|
+
3. **Create modern.config.ts**
|
|
450
|
+
4. **Create src/plugin.config.ts**
|
|
451
|
+
5. **Create src/plugin.ts**
|
|
452
|
+
6. **Create src/App.tsx**
|
|
453
|
+
7. **Create src/pages/MyPluginPage.tsx**
|
|
454
|
+
8. **Create src/components/slots/SidebarWidget.tsx**
|
|
455
|
+
|
|
456
|
+
See [`07-plugin-development.md`](./07-plugin-development.md) for complete step-by-step guide.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## Plugin Development Patterns
|
|
461
|
+
|
|
462
|
+
### CRUD Plugin Pattern (Like Todo)
|
|
463
|
+
|
|
464
|
+
```
|
|
465
|
+
types.ts → Data interfaces
|
|
466
|
+
schemas.ts → Zod validation
|
|
467
|
+
hooks/ → Data fetching (usePocketBase, useItems, useCreate, etc.)
|
|
468
|
+
components/ → List, Item, Form, Dialog components
|
|
469
|
+
pages/ → Main page with filters
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Display Plugin Pattern (Like Dashboard)
|
|
473
|
+
|
|
474
|
+
```
|
|
475
|
+
hooks/ → Stats queries, activity feed
|
|
476
|
+
components/ → Stat cards, activity feed, quick actions
|
|
477
|
+
pages/ → Dashboard overview
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Calculation Plugin Pattern
|
|
481
|
+
|
|
482
|
+
```
|
|
483
|
+
types.ts → Input/Output types
|
|
484
|
+
hooks/ → Pure calculation functions
|
|
485
|
+
components/ → Input form, result display
|
|
486
|
+
pages/ → Calculator page
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## Multi-Tenancy in Plugins
|
|
492
|
+
|
|
493
|
+
**ALL plugin data MUST be scoped to organization:**
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
// ✅ CORRECT - Filter by organization
|
|
497
|
+
const result = await pb.collection('my_items').getList(1, 50, {
|
|
498
|
+
filter: `organizationId = "${orgId}"`,
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// ❌ WRONG - No organization filter
|
|
502
|
+
const result = await pb.collection('my_items').getList(1, 50);
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**PocketBase collections MUST include:**
|
|
506
|
+
- `organizationId` field (relation to organizations)
|
|
507
|
+
- `ownerId` field (relation to users)
|
|
508
|
+
- API rules with organization filtering
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Plugin Best Practices
|
|
513
|
+
|
|
514
|
+
### DO ✅
|
|
515
|
+
|
|
516
|
+
1. **Follow the standard structure** - Copy from Dashboard/Todo plugins
|
|
517
|
+
2. **Use window bridge** for host communication
|
|
518
|
+
3. **Scope all data** to organization
|
|
519
|
+
4. **Handle loading/error states**
|
|
520
|
+
5. **Show toast notifications** for user feedback
|
|
521
|
+
6. **Use existing UI components** from kernel
|
|
522
|
+
7. **Write TypeScript types** for all data
|
|
523
|
+
8. **Validate with Zod schemas**
|
|
524
|
+
|
|
525
|
+
### DON'T ❌
|
|
526
|
+
|
|
527
|
+
1. **Don't import from host** - Use window bridge instead
|
|
528
|
+
2. **Don't skip organization filtering** - Always scope data
|
|
529
|
+
3. **Don't ignore error states** - Handle failures gracefully
|
|
530
|
+
4. **Don't create circular dependencies** - Plugin → Host OK, Host → Plugin bad
|
|
531
|
+
5. **Don't hardcode URLs** - Use environment variables
|
|
532
|
+
6. **Don't forget TypeScript strict mode** - All code must type-check
|
|
533
|
+
7. **Don't skip testing** - Write tests for hooks and components
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## Plugin Registry
|
|
538
|
+
|
|
539
|
+
### How Plugins Are Registered
|
|
540
|
+
|
|
541
|
+
**1. In saas.config.ts (Host config):**
|
|
542
|
+
```typescript
|
|
543
|
+
plugins: [
|
|
544
|
+
{ name: '@lego/plugin-dashboard', enabled: true },
|
|
545
|
+
]
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
**2. In modern.runtime.ts (Garfish config):**
|
|
549
|
+
```typescript
|
|
550
|
+
apps: [
|
|
551
|
+
{
|
|
552
|
+
name: '@lego/plugin-dashboard',
|
|
553
|
+
entry: isDev ? 'http://localhost:3001' : () => import('@lego/plugin-dashboard'),
|
|
554
|
+
activeWhen: '/dashboard',
|
|
555
|
+
},
|
|
556
|
+
]
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**3. In plugin's plugin.config.ts (Plugin manifest):**
|
|
560
|
+
```typescript
|
|
561
|
+
export const pluginConfig: PluginConfig = {
|
|
562
|
+
manifest: { name: '@lego/plugin-dashboard', ... },
|
|
563
|
+
slots: [...],
|
|
564
|
+
routes: [...],
|
|
565
|
+
};
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## Plugin Lifecycle
|
|
571
|
+
|
|
572
|
+
```
|
|
573
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
574
|
+
│ 1. REGISTRATION │
|
|
575
|
+
│ • Plugin added to saas.config.ts │
|
|
576
|
+
│ • Plugin added to modern.runtime.ts │
|
|
577
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
578
|
+
│
|
|
579
|
+
v
|
|
580
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
581
|
+
│ 2. INITIALIZATION │
|
|
582
|
+
│ • Plugin loader reads plugin.config.ts │
|
|
583
|
+
│ • Plugin store creates plugin state │
|
|
584
|
+
│ • Slots registered in slot registry │
|
|
585
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
586
|
+
│
|
|
587
|
+
v
|
|
588
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
589
|
+
│ 3. LOADING (Dev) │
|
|
590
|
+
│ • Plugin starts on dev server (:3001, :3002, etc.) │
|
|
591
|
+
│ • Garfish connects to plugin URL │
|
|
592
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
593
|
+
│
|
|
594
|
+
v
|
|
595
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
596
|
+
│ 4. LOADING (Production) │
|
|
597
|
+
│ • Plugin bundled into host build │
|
|
598
|
+
│ • Garfish dynamically imports plugin │
|
|
599
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
600
|
+
│
|
|
601
|
+
v
|
|
602
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
603
|
+
│ 5. MOUNTING │
|
|
604
|
+
│ • User navigates to plugin route │
|
|
605
|
+
│ • Garfish mounts plugin App component │
|
|
606
|
+
│ • Plugin renders in host layout │
|
|
607
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
608
|
+
│
|
|
609
|
+
v
|
|
610
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
611
|
+
│ 6. ACTIVE │
|
|
612
|
+
│ • Plugin can use host services via channels │
|
|
613
|
+
│ • Plugin slots render in host layout │
|
|
614
|
+
│ • Plugin routes accessible │
|
|
615
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
**Next:** Read [`06-communication-patterns.md`](./06-communication-patterns.md) for inter-plugin communication details.
|