@mostajs/setup 1.5.1 → 2.0.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/dist/api/detect-modules.route.js +1 -1
- package/dist/api/install-modules.route.js +1 -1
- package/dist/api/install.route.d.ts +1 -1
- package/dist/api/install.route.js +1 -1
- package/dist/api/reconfig.route.js +3 -3
- package/dist/api/test-db.route.js +1 -1
- package/dist/api/upload-jar.route.js +1 -1
- package/dist/api/upload-setup-json.route.js +2 -0
- package/dist/cli/init.js +0 -0
- package/dist/components/SetupWizard.d.ts +21 -1
- package/dist/components/SetupWizard.js +25 -7
- package/dist/data/dialects.d.ts +1 -1
- package/dist/index.d.ts +22 -22
- package/dist/index.js +20 -20
- package/dist/lib/compose-uri.d.ts +1 -1
- package/dist/lib/db-test.d.ts +1 -1
- package/dist/lib/db-test.js +1 -1
- package/dist/lib/discover-modules.d.ts +1 -1
- package/dist/lib/discover-modules.js +1 -1
- package/dist/lib/env-writer.d.ts +1 -1
- package/dist/lib/load-setup-json.d.ts +11 -1
- package/dist/lib/load-setup-json.js +7 -2
- package/dist/lib/setup.d.ts +1 -1
- package/dist/lib/setup.js +2 -2
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/schemas/setup.schema.json +39 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @mosta/setup — Detect modules API route factory
|
|
2
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
|
-
import { discoverNpmModules } from '../lib/discover-modules';
|
|
3
|
+
import { discoverNpmModules } from '../lib/discover-modules.js';
|
|
4
4
|
export function createDetectModulesHandler() {
|
|
5
5
|
async function GET() {
|
|
6
6
|
const result = await discoverNpmModules();
|
|
@@ -4,7 +4,7 @@ import { exec } from 'child_process';
|
|
|
4
4
|
import { promisify } from 'util';
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import path from 'path';
|
|
7
|
-
import { MODULES, resolveModuleDependencies } from '../data/module-definitions';
|
|
7
|
+
import { MODULES, resolveModuleDependencies } from '../data/module-definitions.js';
|
|
8
8
|
const execAsync = promisify(exec);
|
|
9
9
|
/**
|
|
10
10
|
* Factory for POST /api/setup/install-modules
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// @mostajs/setup — API Route factory for reconfiguration
|
|
2
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
|
-
import { testDbConnection } from '../lib/db-test';
|
|
4
|
-
import { writeEnvLocal } from '../lib/env-writer';
|
|
5
|
-
import { composeDbUri } from '../lib/compose-uri';
|
|
3
|
+
import { testDbConnection } from '../lib/db-test.js';
|
|
4
|
+
import { writeEnvLocal } from '../lib/env-writer.js';
|
|
5
|
+
import { composeDbUri } from '../lib/compose-uri.js';
|
|
6
6
|
/**
|
|
7
7
|
* Creates handlers for the reconfiguration API.
|
|
8
8
|
*
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
3
|
//
|
|
4
4
|
// Copy to: src/app/api/setup/test-db/route.ts
|
|
5
|
-
import { testDbConnection } from '../lib/db-test';
|
|
5
|
+
import { testDbConnection } from '../lib/db-test.js';
|
|
6
6
|
/**
|
|
7
7
|
* Creates a POST handler for testing DB connections.
|
|
8
8
|
*/
|
|
@@ -376,7 +376,7 @@ export function createUploadJarHandlers() {
|
|
|
376
376
|
if (!dialect) {
|
|
377
377
|
return Response.json({ ok: false, error: 'dialect requis' }, { status: 400 });
|
|
378
378
|
}
|
|
379
|
-
const { composeDbUri } = await import('../lib/compose-uri');
|
|
379
|
+
const { composeDbUri } = await import('../lib/compose-uri.js');
|
|
380
380
|
const uri = composeDbUri(dialect, {
|
|
381
381
|
host: host || 'localhost',
|
|
382
382
|
port: port || 0,
|
|
@@ -25,8 +25,10 @@ export function createSetupJsonHandler(needsSetup) {
|
|
|
25
25
|
exists: true,
|
|
26
26
|
config: {
|
|
27
27
|
appName: json.app?.name,
|
|
28
|
+
dbNamePrefix: json.app?.dbNamePrefix,
|
|
28
29
|
hasRbac: !!(json.rbac?.roles?.length || json.rbac?.permissions?.length),
|
|
29
30
|
seedCount: json.seeds?.length ?? 0,
|
|
31
|
+
modules: json.modules ?? [],
|
|
30
32
|
},
|
|
31
33
|
});
|
|
32
34
|
}
|
package/dist/cli/init.js
CHANGED
|
File without changes
|
|
@@ -18,5 +18,25 @@ export interface SetupWizardProps {
|
|
|
18
18
|
dbNamePrefix?: string;
|
|
19
19
|
/** Whether to persist wizard state in sessionStorage (default: true) */
|
|
20
20
|
persistState?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Show the modules selection step even without a detectModules endpoint.
|
|
23
|
+
* When true and no detectModules endpoint, uses the built-in module list.
|
|
24
|
+
* Default: true
|
|
25
|
+
*/
|
|
26
|
+
showModules?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Full module definitions from setup.json (section "modules").
|
|
29
|
+
* When provided, the wizard shows these modules directly
|
|
30
|
+
* instead of calling the detectModules endpoint.
|
|
31
|
+
*/
|
|
32
|
+
declaredModules?: {
|
|
33
|
+
key: string;
|
|
34
|
+
packageName?: string;
|
|
35
|
+
label?: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
icon?: string;
|
|
38
|
+
required?: boolean;
|
|
39
|
+
dependsOn?: string[];
|
|
40
|
+
}[];
|
|
21
41
|
}
|
|
22
|
-
export default function SetupWizard({ t: tProp, onComplete, endpoints, dbNamePrefix, persistState, }: SetupWizardProps): import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
export default function SetupWizard({ t: tProp, onComplete, endpoints, dbNamePrefix, persistState, showModules, declaredModules, }: SetupWizardProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -344,10 +344,10 @@ function JarUploadInline({ dialect, jarEndpoint, dbConfig }) {
|
|
|
344
344
|
return (_jsxs("div", { style: S.jarBox, children: [_jsxs("div", { style: S.flex(8), children: [_jsx("span", { style: S.jarTitle, children: "Driver JDBC" }), jarStatus?.hasJar ? (_jsx("span", { style: { ...S.badge('installed'), marginLeft: 0 }, children: jarStatus.jarFile })) : (_jsx("span", { style: { fontSize: 12, color: '#6b7280' }, children: "Aucun JAR installe" }))] }), _jsxs("div", { style: { marginTop: 8 }, children: [_jsxs("label", { style: { ...S.btn('primary', uploading), cursor: uploading ? 'wait' : 'pointer', fontSize: 12, padding: '6px 12px' }, children: [uploading ? 'Upload...' : 'Uploader un .jar', _jsx("input", { type: "file", accept: ".jar", onChange: handleUpload, disabled: uploading, style: { display: 'none' } })] }), _jsx("span", { style: { fontSize: 11, color: '#9ca3af', marginLeft: 8 }, children: "Ex: hsqldb*.jar, ojdbc*.jar" })] }), isHsqldb && jarStatus?.hasJar && (_jsxs("div", { style: { marginTop: 10, padding: '10px 12px', backgroundColor: '#fef9c3', borderRadius: 6, border: '1px solid #fde68a' }, children: [_jsxs("div", { style: { fontSize: 12, fontWeight: 600, color: '#92400e', marginBottom: 6 }, children: ["Serveur HSQLDB", _jsxs("span", { style: { fontWeight: 400, color: '#6b7280', marginLeft: 4 }, children: ["(port SGBD : ", dbConfig.port || 9001, ")"] }), serverInfo?.running && (_jsxs("span", { style: { fontWeight: 400, color: '#059669', marginLeft: 8 }, children: ["En marche sur port ", serverInfo.port, serverInfo.pid > 0 ? ` — PID ${serverInfo.pid}` : ''] })), !serverInfo?.running && (_jsx("span", { style: { fontWeight: 400, color: '#dc2626', marginLeft: 8 }, children: "Arrete" }))] }), _jsxs("div", { style: { display: 'flex', gap: 8 }, children: [_jsx("button", { style: btnSmall('#059669', loading === 'start-server' || serverInfo?.running), onClick: () => patchAction({ action: 'start-server', dialect, name: dbConfig.name, host: dbConfig.host, port: dbConfig.port || 9001 }, 'start-server'), disabled: loading === 'start-server' || !!serverInfo?.running, children: loading === 'start-server' ? 'Demarrage...' : `Demarrer le serveur (port ${dbConfig.port || 9001})` }), _jsx("button", { style: btnSmall('#dc2626', loading === 'stop-server' || !serverInfo?.running), onClick: () => patchAction({ action: 'stop-server', port: serverInfo?.port || dbConfig.port || 9001 }, 'stop-server'), disabled: loading === 'stop-server' || !serverInfo?.running, children: loading === 'stop-server' ? 'Arret...' : 'Arreter le serveur' })] })] })), jarStatus?.hasJar && (_jsxs("div", { style: { marginTop: 10, padding: '10px 12px', backgroundColor: '#f0fdf4', borderRadius: 6, border: '1px solid #bbf7d0' }, children: [_jsxs("div", { style: { fontSize: 12, fontWeight: 600, color: '#166534', marginBottom: 6 }, children: ["Bridge JDBC", _jsxs("span", { style: { fontWeight: 400, color: '#6b7280', marginLeft: 4 }, children: ["(port bridge : ", bridgePort || 8765, ")"] }), bridgePort && (_jsxs("span", { style: { fontWeight: 400, color: '#059669', marginLeft: 8 }, children: ["Actif sur port ", bridgePort] })), !bridgePort && bridges.length === 0 && (_jsx("span", { style: { fontWeight: 400, color: '#dc2626', marginLeft: 8 }, children: "Inactif" }))] }), _jsx("div", { style: { display: 'flex', gap: 8, marginBottom: bridges.length > 0 ? 8 : 0 }, children: _jsx("button", { style: btnSmall('#059669', loading === 'start-bridge' || bridges.length > 0), onClick: () => patchAction({ action: 'start', dialect, ...dbConfig }, 'start-bridge'), disabled: loading === 'start-bridge' || bridges.length > 0, children: loading === 'start-bridge' ? 'Lancement...' : `Lancer le bridge (SGBD ${dbConfig.host || 'localhost'}:${dbConfig.port || 9001})` }) }), bridges.map(b => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, padding: '4px 0', fontSize: 12 }, children: [_jsx("span", { style: { width: 8, height: 8, borderRadius: '50%', backgroundColor: b.status === 'active' ? '#22c55e' : '#f59e0b', flexShrink: 0 } }), _jsxs("span", { style: { color: '#374151', fontFamily: 'monospace', fontSize: 11 }, children: ["Bridge port :", b.port, " ", b.pid > 0 ? `(PID ${b.pid})` : '', " ", b.jdbcUrl ? `— ${b.jdbcUrl}` : ''] }), _jsx("button", { style: { ...btnSmall('#dc2626', loading === `kill-${b.port}`), fontSize: 11, padding: '2px 8px', marginLeft: 'auto' }, onClick: () => patchAction({ action: 'stop', port: b.port, pid: b.pid }, `kill-${b.port}`), disabled: loading === `kill-${b.port}`, children: loading === `kill-${b.port}` ? '...' : 'Kill' })] }, b.port)))] })), message && (_jsx("p", { style: { fontSize: 12, color: message.ok ? '#059669' : '#dc2626', marginTop: 8 }, children: message.text }))] }));
|
|
345
345
|
}
|
|
346
346
|
// ── Main Component ───────────────────────────────────────────
|
|
347
|
-
export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNamePrefix = 'mydb', persistState = true, }) {
|
|
347
|
+
export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNamePrefix = 'mydb', persistState = true, showModules = true, declaredModules, }) {
|
|
348
348
|
const t = tProp || ((k) => k);
|
|
349
|
-
// Modules step is
|
|
350
|
-
const hasModulesStep =
|
|
349
|
+
// Modules step is shown unless explicitly disabled
|
|
350
|
+
const hasModulesStep = showModules !== false;
|
|
351
351
|
const STEPS = hasModulesStep
|
|
352
352
|
? ALL_STEPS
|
|
353
353
|
: ALL_STEPS.filter(s => s !== 'modules');
|
|
@@ -416,12 +416,30 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
|
|
|
416
416
|
}
|
|
417
417
|
catch { }
|
|
418
418
|
}, [hydrated, persistState, currentStep, dialect, dbConfig, adminConfig, seedOptions, selectedModules]);
|
|
419
|
-
// --- Detect modules
|
|
419
|
+
// --- Detect modules ---
|
|
420
420
|
useEffect(() => {
|
|
421
|
+
// From setup.json (passed as prop)
|
|
422
|
+
if (declaredModules && declaredModules.length > 0) {
|
|
423
|
+
const mods = declaredModules.map(m => ({
|
|
424
|
+
key: m.key,
|
|
425
|
+
label: m.label ?? m.key,
|
|
426
|
+
description: m.description ?? '',
|
|
427
|
+
icon: m.icon ?? '📦',
|
|
428
|
+
required: m.required,
|
|
429
|
+
default: true,
|
|
430
|
+
dependsOn: m.dependsOn,
|
|
431
|
+
}));
|
|
432
|
+
setAvailableModules(mods);
|
|
433
|
+
setSelectedModules(declaredModules.map(m => m.key));
|
|
434
|
+
setModulesDetected(true);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
// No modules and no endpoint → skip
|
|
421
438
|
if (!ep.detectModules) {
|
|
422
439
|
setModulesDetected(true);
|
|
423
440
|
return;
|
|
424
441
|
}
|
|
442
|
+
// From API endpoint
|
|
425
443
|
fetch(ep.detectModules)
|
|
426
444
|
.then(r => r.json())
|
|
427
445
|
.then((data) => {
|
|
@@ -644,9 +662,9 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
|
|
|
644
662
|
color: mod.type === 'business' ? '#1e40af' : '#6b21a8',
|
|
645
663
|
}, children: mod.type })] }), _jsxs("div", { style: { fontSize: 12, color: '#6b7280', fontFamily: 'monospace' }, children: ["v", mod.version] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 4 }, children: [_jsx("span", { style: { fontSize: 12, fontWeight: 600, color: mod.installed ? '#059669' : '#6b7280' }, children: mod.installed ? 'ON' : 'OFF' }), _jsx("button", { style: S.toggleBtn(mod.installed, wireBusy === mod.name), onClick: (e) => { e.stopPropagation(); handleWireToggle(mod); }, disabled: wireBusy !== null, children: wireBusy === mod.name
|
|
646
664
|
? (mod.installed ? 'Decablage...' : 'Cablage...')
|
|
647
|
-
: (mod.installed ? 'Desinstaller' : 'Installer') })] })] }, mod.name))) }))] }), _jsxs("div", { style: S.navRow, children: [_jsxs("button", { style: S.btn('outline'), onClick: goBack, children: ["\u2190 ", t('setup.back')] }), _jsxs("button", { style: S.btn('primary', !canGoNext()), onClick: goNext, disabled: !canGoNext(), children: [t('setup.next'), " \u2192"] })] })] })), step === 'dialect' && (_jsxs("div", { children: [_jsxs("div", { style: S.sectionHeader, children: [_jsx("span", { style: S.sectionIcon, children: "\uD83D\uDCBE" }), _jsxs("div", { children: [_jsx("div", { style: S.sectionTitle, children: t('setup.dialect.title') }), _jsx("div", { style: S.sectionDesc, children: t('setup.dialect.description') })] })] }), _jsx("div", { style: S.grid3, children: DIALECT_INFO.map(d => (_jsxs("div", { style: S.dialectCard(dialect === d.key, !!d.premium), onClick: () => !d.premium && selectDialect(d.key), title: d.premium ? `${d.name} — disponible en version Premium` : d.name, children: [_jsx("div", { style: S.dialectIcon, children: d.icon }), _jsx("div", { style: S.dialectName, children: d.name }), d.premium && (_jsx("span", { style: { ...S.badge('premium'), marginLeft: 0, marginTop: 4 }, children: "Premium" }))] }, d.key))) }), _jsxs("div", { style: S.navRow, children: [_jsxs("button", { style: S.btn('outline'), onClick: goBack, children: ["\u2190 ", t('setup.back')] }), _jsxs("button", { style: S.btn('primary'), onClick: goNext, children: [t('setup.next'), " \u2192"] })] })] })), step === 'database' && (_jsxs("div", { children: [_jsxs("div", { style: S.sectionHeader, children: [_jsx("span", { style: S.sectionIcon, children: "\uD83D\uDDC4\uFE0F" }), _jsxs("div", { children: [_jsx("div", { style: S.sectionTitle, children: t('setup.database.title') }), _jsx("div", { style: S.sectionDesc, children: t('setup.database.description') })] })] }), dialect === 'sqlite' ? (_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.name') }), _jsx("input", { style: S.input, value: dbConfig.name, onChange: e => { setDbConfig({ ...dbConfig, name: e.target.value }); setDbTestResult(null); }, placeholder: dbNamePrefix }), _jsxs("p", { style: { fontSize: 11, color: '#9ca3af', marginTop: 4 }, children: [t('setup.database.sqliteInfo'), " ", _jsxs("code", { style: { fontFamily: 'monospace', backgroundColor: '#f3f4f6', padding: '1px 4px', borderRadius: 3 }, children: ["./data/", dbConfig.name, ".db"] })] })] })) : dialect === 'spanner' ? (_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.spannerPath') }), _jsx("input", { style: S.input, value: dbConfig.name, onChange: e => { setDbConfig({ ...dbConfig, name: e.target.value }); setDbTestResult(null); }, placeholder: "my-project/my-instance/mydb" }), _jsx("p", { style: { fontSize: 11, color: '#9ca3af', marginTop: 4 }, children: t('setup.database.spannerInfo') })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.host') }), _jsx("input", { style: S.input, value: dbConfig.host, onChange: e => { setDbConfig({ ...dbConfig, host: e.target.value }); setDbTestResult(null); } })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.port') }), _jsx("input", { style: S.input, type: "number", value: dbConfig.port, onChange: e => { setDbConfig({ ...dbConfig, port: parseInt(e.target.value) || 0 }); setDbTestResult(null); } })] })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.name') }), _jsx("input", { style: S.input, value: dbConfig.name, onChange: e => { setDbConfig({ ...dbConfig, name: e.target.value }); setDbTestResult(null); } })] }), _jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.user') }), _jsx("input", { style: S.input, value: dbConfig.user, onChange: e => { setDbConfig({ ...dbConfig, user: e.target.value }); setDbTestResult(null); }, placeholder: dialect === 'hsqldb' ? 'SA' : '' })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.password') }), _jsx("input", { style: S.input, type: "password", value: dbConfig.password, onChange: e => { setDbConfig({ ...dbConfig, password: e.target.value }); setDbTestResult(null); } })] })] })] })), dialect === 'hsqldb' && (_jsxs("div", { style: { ...S.alert('warning'), marginTop: 12, fontSize: 12 }, children: [_jsx("strong", { children: "Prerequis :" }), " Le serveur HSQLDB doit etre lance avant le bridge.", _jsx("br", {}), _jsxs("code", { style: { fontFamily: 'monospace', backgroundColor: '#fef3c7', padding: '2px 6px', borderRadius: 3, display: 'inline-block', marginTop: 4, fontSize: 11 }, children: ["java -cp hsqldb*.jar org.hsqldb.server.Server --database.0 file:./data/", dbConfig.name, " --dbname.0 ", dbConfig.name] })] })), dialect !== 'mongodb' && dialect !== 'sqlite' && (_jsxs("div", { style: { ...S.alert('warning'), marginTop: 12 }, children: [t('setup.database.driverHint'), ' ', _jsx("code", { style: { fontFamily: 'monospace', backgroundColor: '#fef3c7', padding: '1px 4px', borderRadius: 3 }, children: DRIVER_HINTS[dialect] })] })), JDBC_DIALECTS.includes(dialect) && (_jsx(JarUploadInline, { dialect: dialect, jarEndpoint: ep.uploadJar, dbConfig: dbConfig })), dialect !== 'sqlite' && dialect !== 'spanner' && (_jsxs("div", { style: { ...S.checkRow, marginTop: 12, padding: '10px 14px', backgroundColor: '#fffbeb', border: '1px solid #fde68a', borderRadius: 8 }, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: createIfNotExists, onChange: e => setCreateIfNotExists(e.target.checked) }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.database.createIfNotExists') }), _jsx("div", { style: { fontSize: 12, color: '#92400e' }, children: t('setup.database.createIfNotExistsDesc') })] })] })), dialect !== 'sqlite' && dialect !== 'spanner' && (_jsxs("div", { style: { ...S.flex(16), marginTop: 16 }, children: [_jsxs("button", { style: S.btn('outline', dbTesting), onClick: testDb, disabled: dbTesting, children: [dbTesting ? '⏳ ' : '🗄️ ', dbTesting ? t('setup.database.testing') : t('setup.database.test')] }), dbTestResult && (
|
|
648
|
-
|
|
649
|
-
|
|
665
|
+
: (mod.installed ? 'Desinstaller' : 'Installer') })] })] }, mod.name))) }))] }), _jsxs("div", { style: S.navRow, children: [_jsxs("button", { style: S.btn('outline'), onClick: goBack, children: ["\u2190 ", t('setup.back')] }), _jsxs("button", { style: S.btn('primary', !canGoNext()), onClick: goNext, disabled: !canGoNext(), children: [t('setup.next'), " \u2192"] })] })] })), step === 'dialect' && (_jsxs("div", { children: [_jsxs("div", { style: S.sectionHeader, children: [_jsx("span", { style: S.sectionIcon, children: "\uD83D\uDCBE" }), _jsxs("div", { children: [_jsx("div", { style: S.sectionTitle, children: t('setup.dialect.title') }), _jsx("div", { style: S.sectionDesc, children: t('setup.dialect.description') })] })] }), _jsx("div", { style: S.grid3, children: DIALECT_INFO.map(d => (_jsxs("div", { style: S.dialectCard(dialect === d.key, !!d.premium), onClick: () => !d.premium && selectDialect(d.key), title: d.premium ? `${d.name} — disponible en version Premium` : d.name, children: [_jsx("div", { style: S.dialectIcon, children: d.icon }), _jsx("div", { style: S.dialectName, children: d.name }), d.premium && (_jsx("span", { style: { ...S.badge('premium'), marginLeft: 0, marginTop: 4 }, children: "Premium" }))] }, d.key))) }), _jsxs("div", { style: S.navRow, children: [_jsxs("button", { style: S.btn('outline'), onClick: goBack, children: ["\u2190 ", t('setup.back')] }), _jsxs("button", { style: S.btn('primary'), onClick: goNext, children: [t('setup.next'), " \u2192"] })] })] })), step === 'database' && (_jsxs("div", { children: [_jsxs("div", { style: S.sectionHeader, children: [_jsx("span", { style: S.sectionIcon, children: "\uD83D\uDDC4\uFE0F" }), _jsxs("div", { children: [_jsx("div", { style: S.sectionTitle, children: t('setup.database.title') }), _jsx("div", { style: S.sectionDesc, children: t('setup.database.description') })] })] }), dialect === 'sqlite' ? (_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.name') }), _jsx("input", { style: S.input, value: dbConfig.name, onChange: e => { setDbConfig({ ...dbConfig, name: e.target.value }); setDbTestResult(null); }, placeholder: dbNamePrefix }), _jsxs("p", { style: { fontSize: 11, color: '#9ca3af', marginTop: 4 }, children: [t('setup.database.sqliteInfo'), " ", _jsxs("code", { style: { fontFamily: 'monospace', backgroundColor: '#f3f4f6', padding: '1px 4px', borderRadius: 3 }, children: ["./data/", dbConfig.name, ".db"] })] })] })) : dialect === 'spanner' ? (_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.spannerPath') }), _jsx("input", { style: S.input, value: dbConfig.name, onChange: e => { setDbConfig({ ...dbConfig, name: e.target.value }); setDbTestResult(null); }, placeholder: "my-project/my-instance/mydb" }), _jsx("p", { style: { fontSize: 11, color: '#9ca3af', marginTop: 4 }, children: t('setup.database.spannerInfo') })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.host') }), _jsx("input", { style: S.input, value: dbConfig.host, onChange: e => { setDbConfig({ ...dbConfig, host: e.target.value }); setDbTestResult(null); } })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.port') }), _jsx("input", { style: S.input, type: "number", value: dbConfig.port, onChange: e => { setDbConfig({ ...dbConfig, port: parseInt(e.target.value) || 0 }); setDbTestResult(null); } })] })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.name') }), _jsx("input", { style: S.input, value: dbConfig.name, onChange: e => { setDbConfig({ ...dbConfig, name: e.target.value }); setDbTestResult(null); } })] }), _jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.user') }), _jsx("input", { style: S.input, value: dbConfig.user, onChange: e => { setDbConfig({ ...dbConfig, user: e.target.value }); setDbTestResult(null); }, placeholder: dialect === 'hsqldb' ? 'SA' : '' })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.database.password') }), _jsx("input", { style: S.input, type: "password", value: dbConfig.password, onChange: e => { setDbConfig({ ...dbConfig, password: e.target.value }); setDbTestResult(null); } })] })] })] })), dialect === 'hsqldb' && (_jsxs("div", { style: { ...S.alert('warning'), marginTop: 12, fontSize: 12 }, children: [_jsx("strong", { children: "Prerequis :" }), " Le serveur HSQLDB doit etre lance avant le bridge.", _jsx("br", {}), _jsxs("code", { style: { fontFamily: 'monospace', backgroundColor: '#fef3c7', padding: '2px 6px', borderRadius: 3, display: 'inline-block', marginTop: 4, fontSize: 11 }, children: ["java -cp hsqldb*.jar org.hsqldb.server.Server --database.0 file:./data/", dbConfig.name, " --dbname.0 ", dbConfig.name] })] })), dialect !== 'mongodb' && dialect !== 'sqlite' && (_jsxs("div", { style: { ...S.alert('warning'), marginTop: 12 }, children: [t('setup.database.driverHint'), ' ', _jsx("code", { style: { fontFamily: 'monospace', backgroundColor: '#fef3c7', padding: '1px 4px', borderRadius: 3 }, children: DRIVER_HINTS[dialect] })] })), JDBC_DIALECTS.includes(dialect) && (_jsx(JarUploadInline, { dialect: dialect, jarEndpoint: ep.uploadJar, dbConfig: dbConfig })), dialect !== 'sqlite' && dialect !== 'spanner' && (_jsxs("div", { style: { ...S.checkRow, marginTop: 12, padding: '10px 14px', backgroundColor: '#fffbeb', border: '1px solid #fde68a', borderRadius: 8 }, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: createIfNotExists, onChange: e => setCreateIfNotExists(e.target.checked) }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.database.createIfNotExists') }), _jsx("div", { style: { fontSize: 12, color: '#92400e' }, children: t('setup.database.createIfNotExistsDesc') })] })] })), dialect !== 'sqlite' && dialect !== 'spanner' && (_jsxs("div", { style: { ...S.flex(16), marginTop: 16 }, children: [_jsxs("button", { style: S.btn('outline', dbTesting), onClick: testDb, disabled: dbTesting, children: [dbTesting ? '⏳ ' : '🗄️ ', dbTesting ? t('setup.database.testing') : t('setup.database.test')] }), dbTestResult && (_jsxs("div", { style: { fontSize: 13 }, children: [_jsx("span", { style: { color: dbTestResult.ok ? '#059669' : '#dc2626' }, children: dbTestResult.ok
|
|
666
|
+
? `✅ ${t('setup.database.success')}${dbTestResult.dbVersion ? ` (v${dbTestResult.dbVersion})` : ''}`
|
|
667
|
+
: `❌ ${t('setup.database.error')}: ${dbTestResult.error}` }), dbTestResult.ok && (_jsxs("div", { style: { fontSize: 11, color: '#6b7280', marginTop: 4, fontFamily: 'monospace', backgroundColor: '#f3f4f6', padding: '4px 8px', borderRadius: 4 }, children: [dialect, "://", dbConfig.user ? dbConfig.user + '@' : '', dbConfig.host, ":", dbConfig.port, "/", dbConfig.name] }))] }))] })), _jsxs("div", { style: S.navRow, children: [_jsxs("button", { style: S.btn('outline'), onClick: goBack, children: ["\u2190 ", t('setup.back')] }), _jsxs("button", { style: S.btn('primary', !canGoNext()), onClick: goNext, disabled: !canGoNext(), children: [t('setup.next'), " \u2192"] })] })] })), step === 'admin' && (_jsxs("div", { children: [_jsxs("div", { style: S.sectionHeader, children: [_jsx("span", { style: S.sectionIcon, children: "\uD83D\uDC64" }), _jsxs("div", { children: [_jsx("div", { style: S.sectionTitle, children: t('setup.admin.title') }), _jsx("div", { style: S.sectionDesc, children: t('setup.admin.description') })] })] }), _jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.admin.firstName') }), _jsx("input", { style: S.input, value: adminConfig.firstName, onChange: e => setAdminConfig({ ...adminConfig, firstName: e.target.value }) })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.admin.lastName') }), _jsx("input", { style: S.input, value: adminConfig.lastName, onChange: e => setAdminConfig({ ...adminConfig, lastName: e.target.value }) })] })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.admin.email') }), _jsx("input", { style: S.input, type: "email", value: adminConfig.email, onChange: e => setAdminConfig({ ...adminConfig, email: e.target.value }), placeholder: "admin@example.com" })] }), _jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.admin.password') }), _jsx("input", { style: S.input, type: "password", value: adminConfig.password, onChange: e => setAdminConfig({ ...adminConfig, password: e.target.value }) })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: t('setup.admin.confirmPassword') }), _jsx("input", { style: S.input, type: "password", value: adminConfig.confirmPassword, onChange: e => setAdminConfig({ ...adminConfig, confirmPassword: e.target.value }) })] })] }), adminConfig.password && adminConfig.confirmPassword && adminConfig.password !== adminConfig.confirmPassword && (_jsx("p", { style: { fontSize: 13, color: '#dc2626' }, children: t('setup.admin.passwordMismatch') })), _jsxs("div", { style: S.navRow, children: [_jsxs("button", { style: S.btn('outline'), onClick: goBack, children: ["\u2190 ", t('setup.back')] }), _jsxs("button", { style: S.btn('primary', !canGoNext()), onClick: goNext, disabled: !canGoNext(), children: [t('setup.next'), " \u2192"] })] })] })), step === 'summary' && (_jsxs("div", { children: [_jsxs("div", { style: S.sectionHeader, children: [_jsx("span", { style: S.sectionIcon, children: "\u2699\uFE0F" }), _jsxs("div", { children: [_jsx("div", { style: S.sectionTitle, children: t('setup.summary.title') }), _jsx("div", { style: S.sectionDesc, children: t('setup.summary.description') })] })] }), _jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.summary.dbConfig') }), _jsxs("div", { style: S.summaryText, children: [_jsx("span", { style: { fontFamily: 'monospace' }, children: dbSummaryLabel() }), dialect !== 'sqlite' && dbConfig.user && _jsxs("span", { style: { display: 'block', marginTop: 4 }, children: ["Utilisateur: ", dbConfig.user] })] })] }), _jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.summary.adminConfig') }), _jsxs("div", { style: S.summaryText, children: [_jsxs("div", { children: [adminConfig.firstName, " ", adminConfig.lastName] }), _jsx("div", { children: adminConfig.email })] })] }), _jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.modules.title') }), _jsx("div", { style: S.flexWrap, children: selectedModules.map(key => {
|
|
650
668
|
const mod = availableModules.find(m => m.key === key);
|
|
651
669
|
return mod ? (_jsxs("span", { style: {
|
|
652
670
|
display: 'inline-flex', alignItems: 'center', gap: 4,
|
package/dist/data/dialects.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
export { needsSetup, runInstall } from './lib/setup';
|
|
2
|
-
export { testDbConnection } from './lib/db-test';
|
|
3
|
-
export { composeDbUri } from './lib/compose-uri';
|
|
4
|
-
export { writeEnvLocal } from './lib/env-writer';
|
|
5
|
-
export { DIALECT_INFO, ALL_DIALECTS } from './data/dialects';
|
|
6
|
-
export { MODULES, resolveModuleDependencies } from './data/module-definitions';
|
|
7
|
-
export { discoverNpmModules } from './lib/discover-modules';
|
|
8
|
-
export { loadSetupJson } from './lib/load-setup-json';
|
|
9
|
-
export type { SetupJson, SetupJsonRbac, SetupJsonSeed, SetupJsonCategory, SetupJsonPermission, SetupJsonRole } from './lib/load-setup-json';
|
|
10
|
-
export { createTestDbHandler } from './api/test-db.route';
|
|
11
|
-
export { createInstallHandler } from './api/install.route';
|
|
12
|
-
export { createStatusHandler } from './api/status.route';
|
|
13
|
-
export { createDetectModulesHandler } from './api/detect-modules.route';
|
|
14
|
-
export { createInstallModulesHandler } from './api/install-modules.route';
|
|
15
|
-
export { createReconfigHandlers } from './api/reconfig.route';
|
|
16
|
-
export { createUploadJarHandlers } from './api/upload-jar.route';
|
|
17
|
-
export { createWireModuleHandler } from './api/wire-module.route';
|
|
18
|
-
export { createSetupJsonHandler } from './api/upload-setup-json.route';
|
|
19
|
-
export { default as ReconfigPanel } from './components/ReconfigPanel';
|
|
20
|
-
export { default as SetupWizard } from './components/SetupWizard';
|
|
21
|
-
export { setupMenuContribution } from './lib/menu';
|
|
22
|
-
export type { DialectType, DialectInfo, DbConfig, InstallConfig, SeedOptions, SeedDefinition, MostaSetupConfig, ModuleDefinition, } from './types/index';
|
|
1
|
+
export { needsSetup, runInstall } from './lib/setup.js';
|
|
2
|
+
export { testDbConnection } from './lib/db-test.js';
|
|
3
|
+
export { composeDbUri } from './lib/compose-uri.js';
|
|
4
|
+
export { writeEnvLocal } from './lib/env-writer.js';
|
|
5
|
+
export { DIALECT_INFO, ALL_DIALECTS } from './data/dialects.js';
|
|
6
|
+
export { MODULES, resolveModuleDependencies } from './data/module-definitions.js';
|
|
7
|
+
export { discoverNpmModules } from './lib/discover-modules.js';
|
|
8
|
+
export { loadSetupJson } from './lib/load-setup-json.js';
|
|
9
|
+
export type { SetupJson, SetupJsonRbac, SetupJsonSeed, SetupJsonCategory, SetupJsonPermission, SetupJsonRole } from './lib/load-setup-json.js';
|
|
10
|
+
export { createTestDbHandler } from './api/test-db.route.js';
|
|
11
|
+
export { createInstallHandler } from './api/install.route.js';
|
|
12
|
+
export { createStatusHandler } from './api/status.route.js';
|
|
13
|
+
export { createDetectModulesHandler } from './api/detect-modules.route.js';
|
|
14
|
+
export { createInstallModulesHandler } from './api/install-modules.route.js';
|
|
15
|
+
export { createReconfigHandlers } from './api/reconfig.route.js';
|
|
16
|
+
export { createUploadJarHandlers } from './api/upload-jar.route.js';
|
|
17
|
+
export { createWireModuleHandler } from './api/wire-module.route.js';
|
|
18
|
+
export { createSetupJsonHandler } from './api/upload-setup-json.route.js';
|
|
19
|
+
export { default as ReconfigPanel } from './components/ReconfigPanel.js';
|
|
20
|
+
export { default as SetupWizard } from './components/SetupWizard.js';
|
|
21
|
+
export { setupMenuContribution } from './lib/menu.js';
|
|
22
|
+
export type { DialectType, DialectInfo, DbConfig, InstallConfig, SeedOptions, SeedDefinition, MostaSetupConfig, ModuleDefinition, } from './types/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
// @mosta/setup — Barrel exports
|
|
2
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
3
|
// Core
|
|
4
|
-
export { needsSetup, runInstall } from './lib/setup';
|
|
5
|
-
export { testDbConnection } from './lib/db-test';
|
|
6
|
-
export { composeDbUri } from './lib/compose-uri';
|
|
7
|
-
export { writeEnvLocal } from './lib/env-writer';
|
|
4
|
+
export { needsSetup, runInstall } from './lib/setup.js';
|
|
5
|
+
export { testDbConnection } from './lib/db-test.js';
|
|
6
|
+
export { composeDbUri } from './lib/compose-uri.js';
|
|
7
|
+
export { writeEnvLocal } from './lib/env-writer.js';
|
|
8
8
|
// Data
|
|
9
|
-
export { DIALECT_INFO, ALL_DIALECTS } from './data/dialects';
|
|
10
|
-
export { MODULES, resolveModuleDependencies } from './data/module-definitions';
|
|
9
|
+
export { DIALECT_INFO, ALL_DIALECTS } from './data/dialects.js';
|
|
10
|
+
export { MODULES, resolveModuleDependencies } from './data/module-definitions.js';
|
|
11
11
|
// Lib
|
|
12
|
-
export { discoverNpmModules } from './lib/discover-modules';
|
|
13
|
-
export { loadSetupJson } from './lib/load-setup-json';
|
|
12
|
+
export { discoverNpmModules } from './lib/discover-modules.js';
|
|
13
|
+
export { loadSetupJson } from './lib/load-setup-json.js';
|
|
14
14
|
// API route factories
|
|
15
|
-
export { createTestDbHandler } from './api/test-db.route';
|
|
16
|
-
export { createInstallHandler } from './api/install.route';
|
|
17
|
-
export { createStatusHandler } from './api/status.route';
|
|
18
|
-
export { createDetectModulesHandler } from './api/detect-modules.route';
|
|
19
|
-
export { createInstallModulesHandler } from './api/install-modules.route';
|
|
20
|
-
export { createReconfigHandlers } from './api/reconfig.route';
|
|
21
|
-
export { createUploadJarHandlers } from './api/upload-jar.route';
|
|
22
|
-
export { createWireModuleHandler } from './api/wire-module.route';
|
|
23
|
-
export { createSetupJsonHandler } from './api/upload-setup-json.route';
|
|
15
|
+
export { createTestDbHandler } from './api/test-db.route.js';
|
|
16
|
+
export { createInstallHandler } from './api/install.route.js';
|
|
17
|
+
export { createStatusHandler } from './api/status.route.js';
|
|
18
|
+
export { createDetectModulesHandler } from './api/detect-modules.route.js';
|
|
19
|
+
export { createInstallModulesHandler } from './api/install-modules.route.js';
|
|
20
|
+
export { createReconfigHandlers } from './api/reconfig.route.js';
|
|
21
|
+
export { createUploadJarHandlers } from './api/upload-jar.route.js';
|
|
22
|
+
export { createWireModuleHandler } from './api/wire-module.route.js';
|
|
23
|
+
export { createSetupJsonHandler } from './api/upload-setup-json.route.js';
|
|
24
24
|
// Components
|
|
25
|
-
export { default as ReconfigPanel } from './components/ReconfigPanel';
|
|
26
|
-
export { default as SetupWizard } from './components/SetupWizard';
|
|
25
|
+
export { default as ReconfigPanel } from './components/ReconfigPanel.js';
|
|
26
|
+
export { default as SetupWizard } from './components/SetupWizard.js';
|
|
27
27
|
// Menu contribution
|
|
28
|
-
export { setupMenuContribution } from './lib/menu';
|
|
28
|
+
export { setupMenuContribution } from './lib/menu.js';
|
package/dist/lib/db-test.d.ts
CHANGED
package/dist/lib/db-test.js
CHANGED
|
@@ -4,7 +4,7 @@ import { exec } from 'child_process';
|
|
|
4
4
|
import { promisify } from 'util';
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import path from 'path';
|
|
7
|
-
import { MODULES } from '../data/module-definitions';
|
|
7
|
+
import { MODULES } from '../data/module-definitions.js';
|
|
8
8
|
const execAsync = promisify(exec);
|
|
9
9
|
/**
|
|
10
10
|
* Discover @mostajs packages from npm registry and merge with static list.
|
package/dist/lib/env-writer.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MostaSetupConfig } from '../types/index';
|
|
1
|
+
import type { MostaSetupConfig } from '../types/index.js';
|
|
2
2
|
export interface SetupJsonCategory {
|
|
3
3
|
name: string;
|
|
4
4
|
label: string;
|
|
@@ -45,9 +45,19 @@ export interface SetupJson {
|
|
|
45
45
|
dbNamePrefix?: string;
|
|
46
46
|
};
|
|
47
47
|
env?: Record<string, string>;
|
|
48
|
+
modules?: SetupJsonModule[];
|
|
48
49
|
rbac?: SetupJsonRbac;
|
|
49
50
|
seeds?: SetupJsonSeed[];
|
|
50
51
|
}
|
|
52
|
+
export interface SetupJsonModule {
|
|
53
|
+
key: string;
|
|
54
|
+
packageName: string;
|
|
55
|
+
label?: string;
|
|
56
|
+
description?: string;
|
|
57
|
+
icon?: string;
|
|
58
|
+
required?: boolean;
|
|
59
|
+
dependsOn?: string[];
|
|
60
|
+
}
|
|
51
61
|
/**
|
|
52
62
|
* Load a setup.json and return a MostaSetupConfig.
|
|
53
63
|
*
|
|
@@ -58,10 +58,15 @@ function validate(json) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
function buildConfig(json, repoFactory) {
|
|
61
|
+
// Derive MOSTAJS_MODULES from modules[] section
|
|
62
|
+
const extraEnvVars = { ...(json.env ?? {}) };
|
|
63
|
+
if (json.modules?.length) {
|
|
64
|
+
extraEnvVars.MOSTAJS_MODULES = json.modules.map(m => m.key).join(',');
|
|
65
|
+
}
|
|
61
66
|
const config = {
|
|
62
67
|
appName: json.app.name,
|
|
63
68
|
defaultPort: json.app.port,
|
|
64
|
-
extraEnvVars:
|
|
69
|
+
extraEnvVars: Object.keys(extraEnvVars).length > 0 ? extraEnvVars : undefined,
|
|
65
70
|
};
|
|
66
71
|
// ── seedRBAC ───────────────────────────────────────────
|
|
67
72
|
if (json.rbac) {
|
|
@@ -88,7 +93,7 @@ function buildConfig(json, repoFactory) {
|
|
|
88
93
|
if (json.rbac.roles?.length) {
|
|
89
94
|
const roleRepo = await getRepo('role');
|
|
90
95
|
const allPermIds = Object.values(permissionMap);
|
|
91
|
-
for (const roleDef of json.rbac.roles) {
|
|
96
|
+
for (const roleDef of json.rbac.roles.filter(r => r.name)) {
|
|
92
97
|
const permissionIds = roleDef.permissions.includes('*')
|
|
93
98
|
? allPermIds
|
|
94
99
|
: roleDef.permissions.map(code => permissionMap[code]).filter(Boolean);
|
package/dist/lib/setup.d.ts
CHANGED
package/dist/lib/setup.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @mosta/setup — Core setup logic
|
|
2
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
|
-
import { composeDbUri } from './compose-uri';
|
|
4
|
-
import { writeEnvLocal } from './env-writer';
|
|
3
|
+
import { composeDbUri } from './compose-uri.js';
|
|
4
|
+
import { writeEnvLocal } from './env-writer.js';
|
|
5
5
|
/**
|
|
6
6
|
* Check if the app needs initial setup (0 users in DB).
|
|
7
7
|
* Provide a countUsers function from your app.
|
package/dist/types/index.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface InstallConfig {
|
|
|
28
28
|
seed?: SeedOptions;
|
|
29
29
|
modules?: string[];
|
|
30
30
|
}
|
|
31
|
-
export type { ModuleDefinition } from '../data/module-definitions';
|
|
31
|
+
export type { ModuleDefinition } from '../data/module-definitions.js';
|
|
32
32
|
export interface SeedOptions {
|
|
33
33
|
[key: string]: boolean;
|
|
34
34
|
}
|
package/package.json
CHANGED
|
@@ -151,6 +151,45 @@
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
},
|
|
154
|
+
"modules": {
|
|
155
|
+
"type": "array",
|
|
156
|
+
"description": "Modules to use in the project. Exported by MostaSetup Studio. Shown in the wizard with install commands.",
|
|
157
|
+
"items": {
|
|
158
|
+
"type": "object",
|
|
159
|
+
"required": ["key", "packageName"],
|
|
160
|
+
"additionalProperties": false,
|
|
161
|
+
"properties": {
|
|
162
|
+
"key": {
|
|
163
|
+
"type": "string",
|
|
164
|
+
"description": "Unique module identifier (e.g. 'orm', 'auth', 'ticketing')"
|
|
165
|
+
},
|
|
166
|
+
"packageName": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"description": "npm package name (e.g. '@mostajs/orm')"
|
|
169
|
+
},
|
|
170
|
+
"label": {
|
|
171
|
+
"type": "string",
|
|
172
|
+
"description": "Display label"
|
|
173
|
+
},
|
|
174
|
+
"description": {
|
|
175
|
+
"type": "string"
|
|
176
|
+
},
|
|
177
|
+
"icon": {
|
|
178
|
+
"type": "string",
|
|
179
|
+
"description": "Emoji icon"
|
|
180
|
+
},
|
|
181
|
+
"required": {
|
|
182
|
+
"type": "boolean",
|
|
183
|
+
"description": "Whether this module is required (cannot be deselected)"
|
|
184
|
+
},
|
|
185
|
+
"dependsOn": {
|
|
186
|
+
"type": "array",
|
|
187
|
+
"items": { "type": "string" },
|
|
188
|
+
"description": "Module keys this module depends on"
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
},
|
|
154
193
|
"seeds": {
|
|
155
194
|
"type": "array",
|
|
156
195
|
"description": "Optional seed datasets shown as checkboxes in the install wizard",
|