create-lego-one 2.0.20 → 2.0.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-lego-one",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "description": "Create a new Lego-One SaaS application",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.cjs",
@@ -609,9 +609,9 @@ Stay pragmatic. Stay reliable. Keep learning.
609
609
 
610
610
  ```bash
611
611
  # Development
612
- pnpm run dev:all # Start host + all plugins
613
- cd host && pnpm run dev # Start host only (:8080)
614
- cd packages/plugins/@lego/plugin-<name> && pnpm run dev # Start plugin
612
+ pnpm run dev # Start host + all plugins (default)
613
+ pnpm run dev:host # Start host only (:8080) - plugins won't work without their dev servers
614
+ cd packages/plugins/@lego/plugin-<name> && pnpm run dev # Start specific plugin (requires host running)
615
615
 
616
616
  # Building
617
617
  pnpm run build # Build entire project
@@ -604,9 +604,9 @@ Stay pragmatic. Stay reliable. Keep learning.
604
604
 
605
605
  ```bash
606
606
  # Development
607
- pnpm run dev:all # Start host + all plugins
608
- cd host && pnpm run dev # Start host only (:8080)
609
- cd packages/plugins/@lego/plugin-<name> && pnpm run dev # Start plugin
607
+ pnpm run dev # Start host + all plugins (default)
608
+ pnpm run dev:host # Start host only (:8080) - plugins won't work without their dev servers
609
+ cd packages/plugins/@lego/plugin-<name> && pnpm run dev # Start specific plugin (requires host running)
610
610
 
611
611
  # Building
612
612
  pnpm run build # Build entire project
@@ -435,9 +435,9 @@ Use the template from `/docs/checkpoints/.template.md`
435
435
  **Key Commands:**
436
436
  ```bash
437
437
  # Development
438
- pnpm run dev:all # Start all services
439
- pnpm run dev:host # Start host only
440
- pnpm --filter './packages/plugins/@lego/plugin-<name>' dev
438
+ pnpm run dev # Start all services (default)
439
+ pnpm run dev:host # Start host only - plugins won't work without their dev servers
440
+ pnpm --filter './packages/plugins/@lego/plugin-<name>' dev # Start specific plugin (requires host running)
441
441
 
442
442
  # Building
443
443
  pnpm run build # Build all
@@ -13,8 +13,8 @@ This guide covers the complete development workflow for Lego-One, including runn
13
13
  ## Quick Start Commands
14
14
 
15
15
  ```bash
16
- # Start everything (host + all plugins)
17
- pnpm run dev:all
16
+ # Start everything (host + all plugins) - default
17
+ pnpm run dev
18
18
 
19
19
  # Start host only
20
20
  pnpm run dev:host
@@ -39,6 +39,10 @@ pnpm test:e2e
39
39
  ### Running All Services
40
40
 
41
41
  ```bash
42
+ # Start host and all plugins (default)
43
+ pnpm run dev
44
+
45
+ # Alternative (same as above)
42
46
  pnpm run dev:all
43
47
  ```
44
48
 
@@ -57,12 +61,16 @@ pnpm run dev:host
57
61
  cd host && pnpm run dev
58
62
  ```
59
63
 
64
+ **Note:** When running host only, plugins will appear in navigation but won't work without their dev servers running. Use `pnpm run dev` (default) to run everything together.
65
+
60
66
  **Dashboard plugin only:**
61
67
  ```bash
62
68
  cd packages/plugins/@lego/plugin-dashboard
63
69
  pnpm run dev
64
70
  ```
65
71
 
72
+ **Note:** Plugins are micro-frontends that require the host to load them. Running a plugin dev server alone won't work - you need the host running separately.
73
+
66
74
  **Todo plugin only:**
67
75
  ```bash
68
76
  cd packages/plugins/@lego/plugin-todo
@@ -140,8 +140,8 @@ cd pocketbase
140
140
  ### Start Application
141
141
 
142
142
  ```bash
143
- # Start all services
144
- pnpm run dev:all
143
+ # Start all services (default)
144
+ pnpm run dev
145
145
 
146
146
  # Or start host only
147
147
  pnpm run dev:host
@@ -407,7 +407,7 @@ enablePlugin('@lego/plugin-dashboard');
407
407
 
408
408
  ### Development Mode
409
409
  ```bash
410
- pnpm run dev:all
410
+ pnpm run dev
411
411
  ```
412
412
 
413
413
  - Each plugin runs on **separate dev server**
@@ -201,8 +201,8 @@ export default SidebarWidget;
201
201
  ### Dev Mode (Separate Servers)
202
202
 
203
203
  ```bash
204
- # Each plugin runs independently
205
- pnpm run dev:all
204
+ # Each plugin runs independently (default)
205
+ pnpm run dev
206
206
 
207
207
  # Results in:
208
208
  # Host: http://localhost:8080
@@ -207,6 +207,10 @@ After first login:
207
207
  To run the host and all plugins on separate ports:
208
208
 
209
209
  ```bash
210
+ # Start everything (default)
211
+ pnpm run dev
212
+
213
+ # Alternative (same as above)
210
214
  pnpm run dev:all
211
215
  ```
212
216
 
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "host",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "dev": "modern dev",
8
- "build": "modern build",
7
+ "dev": "node scripts/generate-plugin-manifest.js && modern dev",
8
+ "build": "node scripts/generate-plugin-manifest.js && modern build",
9
+ "generate:plugin-manifest": "node scripts/generate-plugin-manifest.js",
9
10
  "start": "modern start",
10
11
  "lint": "eslint src --ext .ts,.tsx",
11
12
  "typecheck": "tsc --noEmit",
@@ -0,0 +1,88 @@
1
+ import { readdir, readFile, writeFile } from 'fs/promises';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+
8
+ /**
9
+ * Scan plugins directory and extract dev server ports from modern.config.ts files
10
+ */
11
+ async function scanPlugins() {
12
+ const pluginsDir = join(__dirname, '../../packages/plugins/@lego');
13
+ const entries = {};
14
+
15
+ try {
16
+ const pluginDirs = await readdir(pluginsDir, { withFileTypes: true });
17
+
18
+ for (const dirent of pluginDirs) {
19
+ if (!dirent.isDirectory() || !dirent.name.startsWith('plugin-')) {
20
+ continue;
21
+ }
22
+
23
+ const pluginName = `@lego/${dirent.name}`;
24
+ const configPath = join(pluginsDir, dirent.name, 'modern.config.ts');
25
+
26
+ try {
27
+ const configContent = await readFile(configPath, 'utf-8');
28
+
29
+ // Extract port from config (look for port: number)
30
+ const portMatch = configContent.match(/port:\s*(\d+)/);
31
+ if (portMatch) {
32
+ const port = parseInt(portMatch[1], 10);
33
+ entries[pluginName] = {
34
+ port,
35
+ url: `http://localhost:${port}`,
36
+ };
37
+ }
38
+ } catch (error) {
39
+ console.warn(`[Plugin Scanner] Could not read config for ${pluginName}:`, error);
40
+ }
41
+ }
42
+ } catch (error) {
43
+ console.error('[Plugin Scanner] Error scanning plugins:', error);
44
+ }
45
+
46
+ return entries;
47
+ }
48
+
49
+ /**
50
+ * Generate TypeScript file with plugin dev entries
51
+ */
52
+ async function generatePluginManifest() {
53
+ const entries = await scanPlugins();
54
+
55
+ const content = `/**
56
+ * Auto-generated plugin dev server manifest
57
+ * This file is generated by scripts/generate-plugin-manifest.js
58
+ * DO NOT EDIT MANUALLY - Run "pnpm run generate:plugin-manifest" to regenerate
59
+ */
60
+
61
+ export const PLUGIN_DEV_ENTRIES: Record<string, string> = ${JSON.stringify(
62
+ Object.fromEntries(
63
+ Object.entries(entries).map(([name, { url }]) => [name, url])
64
+ ),
65
+ null,
66
+ 2
67
+ )};
68
+
69
+ export const PLUGIN_PORTS: Record<string, number> = ${JSON.stringify(
70
+ Object.fromEntries(
71
+ Object.entries(entries).map(([name, { port }]) => [name, port])
72
+ ),
73
+ null,
74
+ 2
75
+ )};
76
+ `;
77
+
78
+ const outputPath = join(__dirname, '../src/kernel/plugins/plugin-manifest.ts');
79
+ await writeFile(outputPath, content, 'utf-8');
80
+
81
+ console.log(`[Plugin Scanner] ✅ Generated manifest with ${Object.keys(entries).length} plugins`);
82
+ if (Object.keys(entries).length > 0) {
83
+ console.log('[Plugin Scanner] Plugins:', Object.keys(entries).join(', '));
84
+ }
85
+ }
86
+
87
+ // Run if executed directly
88
+ generatePluginManifest().catch(console.error);
@@ -0,0 +1,165 @@
1
+ import { useMemo, useEffect, useState } from 'react';
2
+ import { saasConfig } from '../../saas.config';
3
+ import { usePluginStore } from './store';
4
+ import { PLUGIN_DEV_ENTRIES } from './plugin-manifest';
5
+
6
+ /**
7
+ * Plugin route mapping - maps plugin names to their routes
8
+ * This should match modern.runtime.ts configuration
9
+ */
10
+ const PLUGIN_ROUTES: Record<string, { path: string; label: string; icon: string }> = {
11
+ '@lego/plugin-dashboard': {
12
+ path: '/dashboard',
13
+ label: 'Dashboard',
14
+ icon: 'LayoutDashboard',
15
+ },
16
+ '@lego/plugin-todo': {
17
+ path: '/todos',
18
+ label: 'Todos',
19
+ icon: 'CheckSquare',
20
+ },
21
+ };
22
+
23
+ const isDev = import.meta.env.MODE === 'development';
24
+
25
+ /**
26
+ * Check if a plugin dev server is running
27
+ */
28
+ async function checkPluginServerAvailable(entryUrl: string): Promise<boolean> {
29
+ try {
30
+ const controller = new AbortController();
31
+ const timeoutId = setTimeout(() => controller.abort(), 2000); // 2 second timeout
32
+
33
+ const response = await fetch(entryUrl, {
34
+ method: 'GET',
35
+ signal: controller.signal,
36
+ cache: 'no-cache',
37
+ });
38
+
39
+ clearTimeout(timeoutId);
40
+ // If we get any response (even 404), the server is running
41
+ return response.status < 500;
42
+ } catch (error) {
43
+ // Network error or timeout means server is not running
44
+ return false;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Hook to check plugin dev server availability
50
+ */
51
+ function usePluginAvailability() {
52
+ const [availability, setAvailability] = useState<Record<string, boolean>>({});
53
+ const [isChecking, setIsChecking] = useState(true);
54
+
55
+ useEffect(() => {
56
+ if (!isDev) {
57
+ // In production, all enabled plugins are available (bundled)
58
+ const allAvailable: Record<string, boolean> = {};
59
+ saasConfig.plugins.forEach((plugin) => {
60
+ allAvailable[plugin.name] = plugin.enabled;
61
+ });
62
+ setAvailability(allAvailable);
63
+ setIsChecking(false);
64
+ return;
65
+ }
66
+
67
+ // In dev mode, check each plugin's dev server
68
+ const checkAllPlugins = async () => {
69
+ const results: Record<string, boolean> = {};
70
+
71
+ for (const plugin of saasConfig.plugins) {
72
+ if (!plugin.enabled) {
73
+ results[plugin.name] = false;
74
+ continue;
75
+ }
76
+
77
+ // Get dev server URL for this plugin
78
+ const devEntry = PLUGIN_DEV_ENTRIES[plugin.name];
79
+
80
+ if (devEntry) {
81
+ const isAvailable = await checkPluginServerAvailable(devEntry);
82
+ results[plugin.name] = isAvailable;
83
+ } else {
84
+ // Plugin not configured for dev mode
85
+ results[plugin.name] = false;
86
+ }
87
+ }
88
+
89
+ setAvailability(results);
90
+ setIsChecking(false);
91
+ };
92
+
93
+ checkAllPlugins();
94
+
95
+ // Poll every 5 seconds to check if servers come online
96
+ const interval = setInterval(checkAllPlugins, 5000);
97
+
98
+ return () => clearInterval(interval);
99
+ }, []);
100
+
101
+ return { availability, isChecking };
102
+ }
103
+
104
+ /**
105
+ * Hook to get enabled plugins with their route information
106
+ * Only returns plugins that are:
107
+ * 1. Enabled in saas.config.ts
108
+ * 2. Have their dev servers running (in dev mode) OR are bundled (in production)
109
+ */
110
+ export function useEnabledPlugins() {
111
+ const plugins = usePluginStore((state) => state.plugins);
112
+ const { availability, isChecking } = usePluginAvailability();
113
+
114
+ return useMemo(() => {
115
+ // Don't filter while checking (to avoid flickering)
116
+ if (isChecking && isDev) {
117
+ return [];
118
+ }
119
+
120
+ return saasConfig.plugins
121
+ .filter((plugin) => {
122
+ // Only show plugins that are enabled in saas.config.ts
123
+ if (!plugin.enabled) {
124
+ return false;
125
+ }
126
+
127
+ // Check if plugin was explicitly disabled in store (admin override)
128
+ const pluginState = plugins[plugin.name];
129
+ if (pluginState && pluginState.enabled === false) {
130
+ return false;
131
+ }
132
+
133
+ // In dev mode, only show if dev server is available
134
+ if (isDev) {
135
+ return availability[plugin.name] === true;
136
+ }
137
+
138
+ // In production, show if enabled
139
+ return true;
140
+ })
141
+ .map((plugin) => {
142
+ const route = PLUGIN_ROUTES[plugin.name];
143
+ const pluginState = plugins[plugin.name];
144
+
145
+ return {
146
+ name: plugin.name,
147
+ path: route?.path || `/${plugin.name.replace('@lego/plugin-', '')}`,
148
+ label: route?.label || pluginState?.manifest?.displayName || plugin.name,
149
+ icon: route?.icon,
150
+ enabled: plugin.enabled,
151
+ loaded: pluginState?.loaded || false,
152
+ available: availability[plugin.name] ?? false,
153
+ manifest: pluginState?.manifest,
154
+ };
155
+ })
156
+ .filter((plugin) => plugin.path && plugin.label); // Only include plugins with valid routes
157
+ }, [plugins, availability, isChecking]);
158
+ }
159
+
160
+ /**
161
+ * Get plugin route info by name
162
+ */
163
+ export function getPluginRoute(pluginName: string) {
164
+ return PLUGIN_ROUTES[pluginName];
165
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Auto-generated plugin dev server manifest
3
+ * This file is generated by scripts/generate-plugin-manifest.js
4
+ * DO NOT EDIT MANUALLY - Run "pnpm run generate:plugin-manifest" to regenerate
5
+ */
6
+
7
+ export const PLUGIN_DEV_ENTRIES: Record<string, string> = {
8
+ "@lego/plugin-dashboard": "http://localhost:3001",
9
+ "@lego/plugin-todo": "http://localhost:3002"
10
+ };
11
+
12
+ export const PLUGIN_PORTS: Record<string, number> = {
13
+ "@lego/plugin-dashboard": 3001,
14
+ "@lego/plugin-todo": 3002
15
+ };
@@ -20,37 +20,22 @@ export function PocketBaseProvider({ children }: { children: React.ReactNode })
20
20
 
21
21
  const client = new PocketBase(pbUrl);
22
22
 
23
- // Test connection by attempting to fetch API info
24
- setIsChecking(true);
25
- try {
26
- // Try to fetch the API root to verify connection
27
- const response = await fetch(`${pbUrl}/api/health`, {
28
- method: 'GET',
29
- signal: AbortSignal.timeout(5000) // 5 second timeout
30
- });
31
-
32
- if (!response.ok && response.status !== 404) {
33
- throw new Error(`PocketBase returned status ${response.status}`);
34
- }
35
-
36
- console.log('[PocketBaseProvider] ✅ PocketBase connection successful');
37
- } catch (healthError) {
38
- // If health check fails, PocketBase is likely not running
39
- const errorMsg = 'PocketBase is not running or not accessible. Please start PocketBase first.';
40
- console.error('[PocketBaseProvider] ❌ Connection check failed:', healthError);
41
- setError(errorMsg);
42
- setIsChecking(false);
43
- return;
44
- }
23
+ // Skip health check - let PocketBase SDK handle connection errors naturally
24
+ // The client will throw errors when actually used if PocketBase is not available
25
+ setIsChecking(false);
45
26
 
46
27
  // Load stored token
47
28
  const storedToken = localStorage.getItem('pocketbase_auth');
48
29
  if (storedToken) {
49
- client.authStore.save(storedToken, null);
30
+ try {
31
+ client.authStore.save(storedToken, null);
32
+ } catch (tokenError) {
33
+ console.warn('[PocketBaseProvider] Failed to restore auth token:', tokenError);
34
+ localStorage.removeItem('pocketbase_auth');
35
+ }
50
36
  }
51
37
 
52
38
  setPb(client);
53
- setIsChecking(false);
54
39
  console.log('[PocketBaseProvider] ✅ PocketBase client initialized');
55
40
 
56
41
  // Set up auth cleanup on token invalidation
@@ -1,25 +1,44 @@
1
1
  import { Link, NavLink } from '@modern-js/runtime/router';
2
+ import { useMemo } from 'react';
2
3
  import { useGlobalKernelState } from '../kernel/shared-state';
3
4
  import { Slot } from '../kernel/plugins/Slot';
4
5
  import { SlotName } from '../kernel/plugins/types';
6
+ import { useEnabledPlugins } from '../kernel/plugins/hooks';
5
7
  import {
6
8
  Home,
7
- LayoutDashboard,
8
9
  Settings,
9
10
  ChevronLeft,
10
11
  ChevronRight,
12
+ LayoutDashboard,
11
13
  CheckSquare,
14
+ type LucideIcon,
12
15
  } from 'lucide-react';
13
16
  import { cn } from '../kernel/lib/utils';
14
17
 
15
- const navItems = [
18
+ // Icon mapping for plugin icons
19
+ const iconMap: Record<string, LucideIcon> = {
20
+ LayoutDashboard,
21
+ CheckSquare,
22
+ };
23
+
24
+ // Base navigation items (always shown)
25
+ const baseNavItems = [
16
26
  { to: '/', icon: Home, label: 'Home' },
17
- { to: '/dashboard', icon: LayoutDashboard, label: 'Dashboard' },
18
- { to: '/todos', icon: CheckSquare, label: 'Todos' },
19
27
  ];
20
28
 
21
29
  export function Sidebar() {
22
30
  const { sidebarOpen, toggleSidebar, mobileMenuOpen } = useGlobalKernelState();
31
+ const enabledPlugins = useEnabledPlugins();
32
+
33
+ // Build navigation items: base items + enabled plugins
34
+ const navItems = useMemo(() => {
35
+ const pluginNavItems = enabledPlugins.map((plugin) => ({
36
+ to: plugin.path,
37
+ icon: plugin.icon ? iconMap[plugin.icon] : LayoutDashboard,
38
+ label: plugin.label,
39
+ }));
40
+ return [...baseNavItems, ...pluginNavItems];
41
+ }, [enabledPlugins]);
23
42
 
24
43
  return (
25
44
  <>
@@ -1,9 +1,17 @@
1
1
  import { Link } from '@modern-js/runtime/router';
2
- import { ArrowRight, LayoutDashboard, CheckSquare, Shield } from 'lucide-react';
2
+ import { ArrowRight, LayoutDashboard, CheckSquare, Shield, type LucideIcon } from 'lucide-react';
3
3
  import { useGlobalKernelState } from '../kernel/shared-state';
4
+ import { useEnabledPlugins } from '../kernel/plugins/hooks';
5
+
6
+ // Icon mapping for plugin icons
7
+ const iconMap: Record<string, LucideIcon> = {
8
+ LayoutDashboard,
9
+ CheckSquare,
10
+ };
4
11
 
5
12
  export default function HomePage() {
6
13
  const { isAuthenticated, user } = useGlobalKernelState();
14
+ const enabledPlugins = useEnabledPlugins();
7
15
 
8
16
  return (
9
17
  <div className="mx-auto max-w-4xl">
@@ -36,40 +44,30 @@ export default function HomePage() {
36
44
 
37
45
  {/* Features */}
38
46
  <div className="grid gap-6 md:grid-cols-3">
39
- <div className="rounded-xl border bg-card p-6">
40
- <div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
41
- <LayoutDashboard className="h-6 w-6" />
42
- </div>
43
- <h3 className="mt-4 text-lg font-semibold">Dashboard Plugin</h3>
44
- <p className="mt-2 text-sm text-muted-foreground">
45
- Overview of your account with stats and quick actions
46
- </p>
47
- <Link
48
- to="/dashboard"
49
- className="mt-4 inline-flex items-center text-sm font-medium text-primary hover:underline"
50
- >
51
- View Dashboard
52
- <ArrowRight className="ml-1 h-4 w-4" />
53
- </Link>
54
- </div>
55
-
56
- <div className="rounded-xl border bg-card p-6">
57
- <div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
58
- <CheckSquare className="h-6 w-6" />
59
- </div>
60
- <h3 className="mt-4 text-lg font-semibold">Todo Plugin</h3>
61
- <p className="mt-2 text-sm text-muted-foreground">
62
- Manage your tasks with this example plugin
63
- </p>
64
- <Link
65
- to="/todos"
66
- className="mt-4 inline-flex items-center text-sm font-medium text-primary hover:underline"
67
- >
68
- View Todos
69
- <ArrowRight className="ml-1 h-4 w-4" />
70
- </Link>
71
- </div>
47
+ {/* Dynamic plugin cards - only show enabled plugins */}
48
+ {enabledPlugins.map((plugin) => {
49
+ const Icon = plugin.icon ? iconMap[plugin.icon] : LayoutDashboard;
50
+ return (
51
+ <div key={plugin.name} className="rounded-xl border bg-card p-6">
52
+ <div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
53
+ <Icon className="h-6 w-6" />
54
+ </div>
55
+ <h3 className="mt-4 text-lg font-semibold">{plugin.label}</h3>
56
+ <p className="mt-2 text-sm text-muted-foreground">
57
+ {plugin.manifest?.description || `Access the ${plugin.label} plugin`}
58
+ </p>
59
+ <Link
60
+ to={plugin.path}
61
+ className="mt-4 inline-flex items-center text-sm font-medium text-primary hover:underline"
62
+ >
63
+ View {plugin.label}
64
+ <ArrowRight className="ml-1 h-4 w-4" />
65
+ </Link>
66
+ </div>
67
+ );
68
+ })}
72
69
 
70
+ {/* Multi-Tenancy card (always shown) */}
73
71
  <div className="rounded-xl border bg-card p-6">
74
72
  <div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
75
73
  <Shield className="h-6 w-6" />
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "lego-one",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "private": true,
5
5
  "description": "Microkernel SaaS OS with Modern.js + Garfish + PocketBase",
6
6
  "type": "module",
7
7
  "scripts": {
8
- "dev": "pnpm --filter host dev",
8
+ "dev": "pnpm --filter host dev & pnpm -r --filter './packages/plugins/@lego/*' dev",
9
9
  "dev:host": "pnpm --filter host dev",
10
- "dev:plugins": "pnpm -r --filter './packages/plugins/@lego/*' dev",
11
10
  "dev:all": "pnpm --filter host dev & pnpm -r --filter './packages/plugins/@lego/*' dev",
12
11
  "build": "pnpm --filter host build && pnpm -r --filter './packages/plugins/@lego/*' build",
13
12
  "lint": "pnpm -r lint",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lego/plugin-dashboard",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lego/plugin-todo",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {