create-fluxstack 1.4.1 → 1.5.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/.env.example +8 -1
- package/CRYPTO-AUTH-MIDDLEWARE-GUIDE.md +475 -0
- package/CRYPTO-AUTH-MIDDLEWARES.md +473 -0
- package/CRYPTO-AUTH-USAGE.md +491 -0
- package/EXEMPLO-ROTA-PROTEGIDA.md +347 -0
- package/QUICK-START-CRYPTO-AUTH.md +221 -0
- package/app/client/src/App.tsx +4 -1
- package/app/client/src/pages/CryptoAuthPage.tsx +394 -0
- package/app/server/index.ts +4 -0
- package/app/server/routes/crypto-auth-demo.routes.ts +167 -0
- package/app/server/routes/example-with-crypto-auth.routes.ts +235 -0
- package/app/server/routes/exemplo-posts.routes.ts +161 -0
- package/app/server/routes/index.ts +5 -1
- package/config/index.ts +9 -1
- package/core/cli/generators/plugin.ts +324 -34
- package/core/cli/generators/template-engine.ts +5 -0
- package/core/cli/plugin-discovery.ts +33 -12
- package/core/framework/server.ts +10 -0
- package/core/plugins/dependency-manager.ts +89 -22
- package/core/plugins/index.ts +4 -0
- package/core/plugins/manager.ts +3 -2
- package/core/plugins/module-resolver.ts +216 -0
- package/core/plugins/registry.ts +28 -1
- package/core/utils/logger/index.ts +4 -0
- package/fluxstack.config.ts +253 -114
- package/package.json +117 -117
- package/plugins/crypto-auth/README.md +722 -172
- package/plugins/crypto-auth/ai-context.md +1282 -0
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +383 -0
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +136 -159
- package/plugins/crypto-auth/client/components/AuthProvider.tsx +35 -94
- package/plugins/crypto-auth/client/components/LoginButton.tsx +36 -53
- package/plugins/crypto-auth/client/components/ProtectedRoute.tsx +17 -37
- package/plugins/crypto-auth/client/components/index.ts +1 -4
- package/plugins/crypto-auth/client/index.ts +1 -1
- package/plugins/crypto-auth/config/index.ts +34 -0
- package/plugins/crypto-auth/index.ts +84 -152
- package/plugins/crypto-auth/package.json +65 -64
- package/plugins/crypto-auth/server/AuthMiddleware.ts +19 -75
- package/plugins/crypto-auth/server/CryptoAuthService.ts +60 -167
- package/plugins/crypto-auth/server/index.ts +15 -2
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +65 -0
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +26 -0
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +76 -0
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +45 -0
- package/plugins/crypto-auth/server/middlewares/helpers.ts +140 -0
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -0
- package/plugins/crypto-auth/server/middlewares.ts +19 -0
- package/test-crypto-auth.ts +101 -0
- package/plugins/crypto-auth/client/components/SessionInfo.tsx +0 -242
- package/plugins/crypto-auth/plugin.json +0 -29
|
@@ -136,6 +136,8 @@ export class PluginDependencyManager {
|
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
138
|
* Instalar dependências de plugins
|
|
139
|
+
* NOVA ESTRATÉGIA: Instala no node_modules local do plugin primeiro,
|
|
140
|
+
* com fallback para o projeto principal
|
|
139
141
|
*/
|
|
140
142
|
async installPluginDependencies(resolutions: DependencyResolution[]): Promise<void> {
|
|
141
143
|
if (!this.config.autoInstall) {
|
|
@@ -143,44 +145,109 @@ export class PluginDependencyManager {
|
|
|
143
145
|
return
|
|
144
146
|
}
|
|
145
147
|
|
|
146
|
-
|
|
147
|
-
const conflicts: DependencyConflict[] = []
|
|
148
|
-
|
|
149
|
-
// Coletar todas as dependências e conflitos
|
|
148
|
+
// Instalar dependências para cada plugin individualmente
|
|
150
149
|
for (const resolution of resolutions) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
if (resolution.dependencies.length === 0) continue
|
|
151
|
+
|
|
152
|
+
const pluginPath = this.findPluginDirectory(resolution.plugin)
|
|
153
|
+
if (!pluginPath) {
|
|
154
|
+
this.logger?.warn(`Não foi possível encontrar diretório do plugin '${resolution.plugin}'`)
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
154
157
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
+
this.logger?.debug(`📦 Instalando dependências localmente para plugin '${resolution.plugin}'`, {
|
|
159
|
+
plugin: resolution.plugin,
|
|
160
|
+
path: pluginPath,
|
|
161
|
+
dependencies: resolution.dependencies.length
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
// Instalar APENAS no node_modules local do plugin
|
|
166
|
+
await this.installPluginDependenciesLocally(pluginPath, resolution.dependencies)
|
|
167
|
+
|
|
168
|
+
this.logger?.debug(`✅ Dependências do plugin '${resolution.plugin}' instaladas localmente`)
|
|
169
|
+
} catch (error) {
|
|
170
|
+
this.logger?.error(`❌ Erro ao instalar dependências do plugin '${resolution.plugin}'`, { error })
|
|
171
|
+
// Continuar com outros plugins
|
|
172
|
+
}
|
|
158
173
|
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Instalar dependências no diretório local do plugin
|
|
178
|
+
*/
|
|
179
|
+
private async installPluginDependenciesLocally(pluginPath: string, dependencies: PluginDependency[]): Promise<void> {
|
|
180
|
+
if (dependencies.length === 0) return
|
|
181
|
+
|
|
182
|
+
const regularDeps = dependencies.filter(d => d.type === 'dependency')
|
|
183
|
+
const peerDeps = dependencies.filter(d => d.type === 'peerDependency' && !d.optional)
|
|
184
|
+
|
|
185
|
+
const allDeps = [...regularDeps, ...peerDeps]
|
|
186
|
+
if (allDeps.length === 0) return
|
|
159
187
|
|
|
160
|
-
//
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
|
|
188
|
+
// Verificar quais dependências já estão instaladas localmente
|
|
189
|
+
const toInstall = allDeps.filter(dep => {
|
|
190
|
+
const depPath = join(pluginPath, 'node_modules', dep.name, 'package.json')
|
|
191
|
+
if (!existsSync(depPath)) {
|
|
192
|
+
return true // Precisa instalar
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const installedPkg = JSON.parse(readFileSync(depPath, 'utf-8'))
|
|
197
|
+
const installedVersion = installedPkg.version
|
|
198
|
+
|
|
199
|
+
// Verificar se a versão é compatível
|
|
200
|
+
if (!this.isVersionCompatible(installedVersion, dep.version)) {
|
|
201
|
+
this.logger?.debug(`📦 Dependência '${dep.name}' está desatualizada (${installedVersion} → ${dep.version})`)
|
|
202
|
+
return true // Precisa atualizar
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return false // Já está instalado corretamente
|
|
206
|
+
} catch (error) {
|
|
207
|
+
return true // Erro ao ler, melhor reinstalar
|
|
208
|
+
}
|
|
164
209
|
})
|
|
165
210
|
|
|
166
|
-
if (
|
|
167
|
-
this.logger?.debug(
|
|
211
|
+
if (toInstall.length === 0) {
|
|
212
|
+
this.logger?.debug(`✅ Todas as dependências do plugin já estão instaladas`)
|
|
168
213
|
return
|
|
169
214
|
}
|
|
170
215
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
216
|
+
const packages = toInstall.map(d => `${d.name}@${d.version}`).join(' ')
|
|
217
|
+
const command = this.getInstallCommand(packages, false)
|
|
218
|
+
|
|
219
|
+
this.logger?.debug(`🔧 Instalando ${toInstall.length} dependência(s): ${command}`, { cwd: pluginPath })
|
|
174
220
|
|
|
175
221
|
try {
|
|
176
|
-
|
|
177
|
-
|
|
222
|
+
execSync(command, {
|
|
223
|
+
cwd: pluginPath,
|
|
224
|
+
stdio: 'inherit'
|
|
225
|
+
})
|
|
226
|
+
this.logger?.debug(`✅ Pacotes instalados localmente em ${pluginPath}`)
|
|
178
227
|
} catch (error) {
|
|
179
|
-
this.logger?.error(
|
|
228
|
+
this.logger?.error(`❌ Falha ao instalar dependências localmente`, { error, pluginPath })
|
|
180
229
|
throw error
|
|
181
230
|
}
|
|
182
231
|
}
|
|
183
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Encontrar diretório de um plugin pelo nome
|
|
235
|
+
*/
|
|
236
|
+
private findPluginDirectory(pluginName: string): string | null {
|
|
237
|
+
const possiblePaths = [
|
|
238
|
+
`plugins/${pluginName}`,
|
|
239
|
+
`core/plugins/built-in/${pluginName}`
|
|
240
|
+
]
|
|
241
|
+
|
|
242
|
+
for (const path of possiblePaths) {
|
|
243
|
+
if (existsSync(path)) {
|
|
244
|
+
return resolve(path)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return null
|
|
249
|
+
}
|
|
250
|
+
|
|
184
251
|
/**
|
|
185
252
|
* Detectar conflitos de versão
|
|
186
253
|
*/
|
package/core/plugins/index.ts
CHANGED
|
@@ -54,6 +54,10 @@ export {
|
|
|
54
54
|
} from './manager'
|
|
55
55
|
export type { PluginManagerConfig } from './manager'
|
|
56
56
|
|
|
57
|
+
// Module resolver for plugins
|
|
58
|
+
export { PluginModuleResolver } from './module-resolver'
|
|
59
|
+
export type { ModuleResolverConfig } from './module-resolver'
|
|
60
|
+
|
|
57
61
|
// Plugin executor
|
|
58
62
|
export {
|
|
59
63
|
PluginExecutor,
|
package/core/plugins/manager.ts
CHANGED
|
@@ -19,7 +19,7 @@ import type {
|
|
|
19
19
|
|
|
20
20
|
type Plugin = FluxStack.Plugin
|
|
21
21
|
import type { FluxStackConfig } from "../config/schema"
|
|
22
|
-
import type { Logger } from "../utils/logger
|
|
22
|
+
import type { Logger } from "../utils/logger"
|
|
23
23
|
import { PluginRegistry } from "./registry"
|
|
24
24
|
import { createPluginUtils } from "./config"
|
|
25
25
|
import { FluxStackError } from "../utils/errors"
|
|
@@ -422,6 +422,7 @@ export class PluginManager extends EventEmitter {
|
|
|
422
422
|
// Try to use auto-generated registry for external plugins (if available from build)
|
|
423
423
|
let externalResults: any[] = []
|
|
424
424
|
try {
|
|
425
|
+
// @ts-expect-error - auto-registry is generated during build, may not exist in dev
|
|
425
426
|
const autoRegistryModule = await import('./auto-registry')
|
|
426
427
|
if (autoRegistryModule.discoveredPlugins && autoRegistryModule.registerDiscoveredPlugins) {
|
|
427
428
|
this.logger.debug('🚀 Using auto-generated external plugins registry')
|
|
@@ -432,7 +433,7 @@ export class PluginManager extends EventEmitter {
|
|
|
432
433
|
}))
|
|
433
434
|
}
|
|
434
435
|
} catch (error) {
|
|
435
|
-
this.logger.debug('Auto-generated external plugins registry not found, falling back to discovery', { error: error.message })
|
|
436
|
+
this.logger.debug('Auto-generated external plugins registry not found, falling back to discovery', { error: (error as Error).message })
|
|
436
437
|
|
|
437
438
|
// Fallback to runtime discovery for external plugins
|
|
438
439
|
this.logger.debug('Discovering external plugins in directory: plugins')
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module Resolver para Plugins
|
|
3
|
+
* Implementa resolução em cascata: plugin local → projeto principal
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync } from 'fs'
|
|
7
|
+
import { join, resolve } from 'path'
|
|
8
|
+
import type { Logger } from '../utils/logger'
|
|
9
|
+
|
|
10
|
+
export interface ModuleResolverConfig {
|
|
11
|
+
projectRoot: string
|
|
12
|
+
logger?: Logger
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class PluginModuleResolver {
|
|
16
|
+
private config: ModuleResolverConfig
|
|
17
|
+
private logger?: Logger
|
|
18
|
+
private resolveCache: Map<string, string> = new Map()
|
|
19
|
+
|
|
20
|
+
constructor(config: ModuleResolverConfig) {
|
|
21
|
+
this.config = config
|
|
22
|
+
this.logger = config.logger
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolve um módulo com estratégia em cascata:
|
|
27
|
+
* 1. node_modules local do plugin
|
|
28
|
+
* 2. node_modules do projeto principal
|
|
29
|
+
*/
|
|
30
|
+
resolveModule(moduleName: string, pluginPath: string): string | null {
|
|
31
|
+
const cacheKey = `${pluginPath}::${moduleName}`
|
|
32
|
+
|
|
33
|
+
// Verificar cache
|
|
34
|
+
if (this.resolveCache.has(cacheKey)) {
|
|
35
|
+
return this.resolveCache.get(cacheKey)!
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.logger?.debug(`Resolvendo módulo '${moduleName}' para plugin em '${pluginPath}'`)
|
|
39
|
+
|
|
40
|
+
// 1. Tentar no node_modules local do plugin
|
|
41
|
+
const localPath = this.tryResolveLocal(moduleName, pluginPath)
|
|
42
|
+
if (localPath) {
|
|
43
|
+
this.logger?.debug(`✅ Módulo '${moduleName}' encontrado localmente: ${localPath}`)
|
|
44
|
+
this.resolveCache.set(cacheKey, localPath)
|
|
45
|
+
return localPath
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 2. Tentar no node_modules do projeto principal
|
|
49
|
+
const projectPath = this.tryResolveProject(moduleName)
|
|
50
|
+
if (projectPath) {
|
|
51
|
+
this.logger?.debug(`✅ Módulo '${moduleName}' encontrado no projeto: ${projectPath}`)
|
|
52
|
+
this.resolveCache.set(cacheKey, projectPath)
|
|
53
|
+
return projectPath
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.logger?.warn(`❌ Módulo '${moduleName}' não encontrado em nenhum contexto`)
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Tenta resolver no node_modules local do plugin
|
|
62
|
+
*/
|
|
63
|
+
private tryResolveLocal(moduleName: string, pluginPath: string): string | null {
|
|
64
|
+
const pluginDir = resolve(pluginPath)
|
|
65
|
+
const localNodeModules = join(pluginDir, 'node_modules', moduleName)
|
|
66
|
+
|
|
67
|
+
if (existsSync(localNodeModules)) {
|
|
68
|
+
// Verificar se tem package.json para pegar o entry point
|
|
69
|
+
const packageJsonPath = join(localNodeModules, 'package.json')
|
|
70
|
+
if (existsSync(packageJsonPath)) {
|
|
71
|
+
try {
|
|
72
|
+
const pkg = require(packageJsonPath)
|
|
73
|
+
const entry = pkg.module || pkg.main || 'index.js'
|
|
74
|
+
const entryPath = join(localNodeModules, entry)
|
|
75
|
+
|
|
76
|
+
if (existsSync(entryPath)) {
|
|
77
|
+
return entryPath
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
this.logger?.debug(`Erro ao ler package.json de '${moduleName}'`, { error })
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Fallback: tentar index.js/index.ts
|
|
85
|
+
const indexJs = join(localNodeModules, 'index.js')
|
|
86
|
+
const indexTs = join(localNodeModules, 'index.ts')
|
|
87
|
+
|
|
88
|
+
if (existsSync(indexJs)) return indexJs
|
|
89
|
+
if (existsSync(indexTs)) return indexTs
|
|
90
|
+
|
|
91
|
+
return localNodeModules
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return null
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Tenta resolver no node_modules do projeto principal
|
|
99
|
+
*/
|
|
100
|
+
private tryResolveProject(moduleName: string): string | null {
|
|
101
|
+
const projectNodeModules = join(this.config.projectRoot, 'node_modules', moduleName)
|
|
102
|
+
|
|
103
|
+
if (existsSync(projectNodeModules)) {
|
|
104
|
+
// Verificar se tem package.json para pegar o entry point
|
|
105
|
+
const packageJsonPath = join(projectNodeModules, 'package.json')
|
|
106
|
+
if (existsSync(packageJsonPath)) {
|
|
107
|
+
try {
|
|
108
|
+
const pkg = require(packageJsonPath)
|
|
109
|
+
const entry = pkg.module || pkg.main || 'index.js'
|
|
110
|
+
const entryPath = join(projectNodeModules, entry)
|
|
111
|
+
|
|
112
|
+
if (existsSync(entryPath)) {
|
|
113
|
+
return entryPath
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
this.logger?.debug(`Erro ao ler package.json de '${moduleName}'`, { error })
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Fallback: tentar index.js/index.ts
|
|
121
|
+
const indexJs = join(projectNodeModules, 'index.js')
|
|
122
|
+
const indexTs = join(projectNodeModules, 'index.ts')
|
|
123
|
+
|
|
124
|
+
if (existsSync(indexJs)) return indexJs
|
|
125
|
+
if (existsSync(indexTs)) return indexTs
|
|
126
|
+
|
|
127
|
+
return projectNodeModules
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Resolve sub-paths (ex: @noble/curves/ed25519)
|
|
135
|
+
*/
|
|
136
|
+
resolveSubpath(moduleName: string, subpath: string, pluginPath: string): string | null {
|
|
137
|
+
const fullModule = `${moduleName}/${subpath}`
|
|
138
|
+
const cacheKey = `${pluginPath}::${fullModule}`
|
|
139
|
+
|
|
140
|
+
// Verificar cache
|
|
141
|
+
if (this.resolveCache.has(cacheKey)) {
|
|
142
|
+
return this.resolveCache.get(cacheKey)!
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.logger?.debug(`Resolvendo subpath '${fullModule}' para plugin em '${pluginPath}'`)
|
|
146
|
+
|
|
147
|
+
// 1. Tentar no node_modules local do plugin
|
|
148
|
+
const pluginDir = resolve(pluginPath)
|
|
149
|
+
const localPath = join(pluginDir, 'node_modules', fullModule)
|
|
150
|
+
|
|
151
|
+
if (this.existsWithExtension(localPath)) {
|
|
152
|
+
const resolvedLocal = this.findFileWithExtension(localPath)
|
|
153
|
+
if (resolvedLocal) {
|
|
154
|
+
this.logger?.debug(`✅ Subpath '${fullModule}' encontrado localmente: ${resolvedLocal}`)
|
|
155
|
+
this.resolveCache.set(cacheKey, resolvedLocal)
|
|
156
|
+
return resolvedLocal
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 2. Tentar no node_modules do projeto principal
|
|
161
|
+
const projectPath = join(this.config.projectRoot, 'node_modules', fullModule)
|
|
162
|
+
|
|
163
|
+
if (this.existsWithExtension(projectPath)) {
|
|
164
|
+
const resolvedProject = this.findFileWithExtension(projectPath)
|
|
165
|
+
if (resolvedProject) {
|
|
166
|
+
this.logger?.debug(`✅ Subpath '${fullModule}' encontrado no projeto: ${resolvedProject}`)
|
|
167
|
+
this.resolveCache.set(cacheKey, resolvedProject)
|
|
168
|
+
return resolvedProject
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.logger?.warn(`❌ Subpath '${fullModule}' não encontrado em nenhum contexto`)
|
|
173
|
+
return null
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Verifica se arquivo existe com alguma extensão comum
|
|
178
|
+
*/
|
|
179
|
+
private existsWithExtension(basePath: string): boolean {
|
|
180
|
+
const extensions = ['', '.js', '.ts', '.mjs', '.cjs', '.jsx', '.tsx', '/index.js', '/index.ts']
|
|
181
|
+
return extensions.some(ext => existsSync(basePath + ext))
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Encontra arquivo com extensão
|
|
186
|
+
*/
|
|
187
|
+
private findFileWithExtension(basePath: string): string | null {
|
|
188
|
+
const extensions = ['', '.js', '.ts', '.mjs', '.cjs', '.jsx', '.tsx', '/index.js', '/index.ts']
|
|
189
|
+
|
|
190
|
+
for (const ext of extensions) {
|
|
191
|
+
const fullPath = basePath + ext
|
|
192
|
+
if (existsSync(fullPath)) {
|
|
193
|
+
return fullPath
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return null
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Limpar cache
|
|
202
|
+
*/
|
|
203
|
+
clearCache(): void {
|
|
204
|
+
this.resolveCache.clear()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Obter estatísticas
|
|
209
|
+
*/
|
|
210
|
+
getStats() {
|
|
211
|
+
return {
|
|
212
|
+
cachedModules: this.resolveCache.size,
|
|
213
|
+
projectRoot: this.config.projectRoot
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
package/core/plugins/registry.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { FluxStack, PluginManifest, PluginLoadResult, PluginDiscoveryOption
|
|
|
2
2
|
|
|
3
3
|
type FluxStackPlugin = FluxStack.Plugin
|
|
4
4
|
import type { FluxStackConfig } from "../config/schema"
|
|
5
|
-
import type { Logger } from "../utils/logger
|
|
5
|
+
import type { Logger } from "../utils/logger"
|
|
6
6
|
import { FluxStackError } from "../utils/errors"
|
|
7
7
|
import { PluginDependencyManager } from "./dependency-manager"
|
|
8
8
|
import { readdir, readFile } from "fs/promises"
|
|
@@ -262,6 +262,33 @@ export class PluginRegistry {
|
|
|
262
262
|
if (existsSync(manifestPath)) {
|
|
263
263
|
const manifestContent = await readFile(manifestPath, 'utf-8')
|
|
264
264
|
manifest = JSON.parse(manifestContent)
|
|
265
|
+
} else {
|
|
266
|
+
// Try package.json for npm plugins
|
|
267
|
+
const packagePath = join(pluginPath, 'package.json')
|
|
268
|
+
if (existsSync(packagePath)) {
|
|
269
|
+
try {
|
|
270
|
+
const packageContent = await readFile(packagePath, 'utf-8')
|
|
271
|
+
const packageJson = JSON.parse(packageContent)
|
|
272
|
+
|
|
273
|
+
if (packageJson.fluxstack) {
|
|
274
|
+
manifest = {
|
|
275
|
+
name: packageJson.name,
|
|
276
|
+
version: packageJson.version,
|
|
277
|
+
description: packageJson.description || '',
|
|
278
|
+
author: packageJson.author || '',
|
|
279
|
+
license: packageJson.license || '',
|
|
280
|
+
homepage: packageJson.homepage,
|
|
281
|
+
repository: packageJson.repository,
|
|
282
|
+
keywords: packageJson.keywords || [],
|
|
283
|
+
dependencies: packageJson.dependencies || {},
|
|
284
|
+
peerDependencies: packageJson.peerDependencies,
|
|
285
|
+
fluxstack: packageJson.fluxstack
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} catch (error) {
|
|
289
|
+
this.logger?.warn(`Failed to parse package.json in '${pluginPath}'`, { error })
|
|
290
|
+
}
|
|
291
|
+
}
|
|
265
292
|
}
|
|
266
293
|
|
|
267
294
|
// Try to import the plugin
|
|
@@ -23,6 +23,10 @@ export { clearColorCache } from './colors'
|
|
|
23
23
|
export { clearCallerCache } from './stack-trace'
|
|
24
24
|
export { clearLoggerCache } from './winston-logger'
|
|
25
25
|
|
|
26
|
+
// Export Logger type from winston
|
|
27
|
+
import type winston from 'winston'
|
|
28
|
+
export type Logger = winston.Logger
|
|
29
|
+
|
|
26
30
|
// Re-export banner utilities for custom banners
|
|
27
31
|
export { displayStartupBanner, type StartupInfo } from './startup-banner'
|
|
28
32
|
|