@mostajs/setup 2.1.2 → 2.1.4

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.
@@ -28,6 +28,13 @@ export function createSetupJsonHandler(needsSetup) {
28
28
  dbNamePrefix: json.app?.dbNamePrefix,
29
29
  hasRbac: !!(json.rbac?.roles?.length || json.rbac?.permissions?.length),
30
30
  seedCount: json.seeds?.length ?? 0,
31
+ seeds: (json.seeds ?? []).map(s => ({
32
+ key: s.key,
33
+ label: s.label,
34
+ description: s.description ?? '',
35
+ icon: s.icon,
36
+ default: s.default ?? false,
37
+ })),
31
38
  modules: json.modules ?? [],
32
39
  },
33
40
  });
@@ -17,6 +17,8 @@ export interface SetupWizardProps {
17
17
  preflight?: string;
18
18
  /** Create database endpoint */
19
19
  createDb?: string;
20
+ /** Setup JSON endpoint (GET returns seeds list) */
21
+ setupJson?: string;
20
22
  };
21
23
  /** Default database name prefix (e.g. 'secuaccessdb') */
22
24
  dbNamePrefix?: string;
@@ -361,6 +361,7 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
361
361
  seed: endpoints.seed || '',
362
362
  preflight: endpoints.preflight || '/api/setup/preflight',
363
363
  createDb: endpoints.createDb || '/api/setup/create-db',
364
+ setupJson: endpoints.setupJson || '/api/setup/setup-json',
364
365
  };
365
366
  // --- State ---
366
367
  const [currentStep, setCurrentStep] = useState(0);
@@ -369,7 +370,8 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
369
370
  const [dbTestResult, setDbTestResult] = useState(null);
370
371
  const [dbTesting, setDbTesting] = useState(false);
371
372
  const [adminConfig, setAdminConfig] = useState({ firstName: '', lastName: '', email: '', password: '', confirmPassword: '' });
372
- const [seedOptions, setSeedOptions] = useState({ activities: true, demoUsers: false, demoData: false });
373
+ const [seedOptions, setSeedOptions] = useState({});
374
+ const [availableSeeds, setAvailableSeeds] = useState([]);
373
375
  const [availableModules, setAvailableModules] = useState([]);
374
376
  const [selectedModules, setSelectedModules] = useState([]);
375
377
  const [detectedModules, setDetectedModules] = useState([]);
@@ -423,6 +425,27 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
423
425
  }
424
426
  catch { }
425
427
  }, [hydrated, persistState, currentStep, dialect, dbConfig, adminConfig, seedOptions, selectedModules]);
428
+ // --- Load seeds from setup.json ---
429
+ useEffect(() => {
430
+ if (!hydrated)
431
+ return;
432
+ fetch(ep.setupJson)
433
+ .then(r => r.json())
434
+ .then((data) => {
435
+ const seeds = data.config?.seeds ?? [];
436
+ setAvailableSeeds(seeds);
437
+ // Initialize seedOptions from defaults (only if not already restored from session)
438
+ setSeedOptions(prev => {
439
+ if (Object.keys(prev).length > 0)
440
+ return prev;
441
+ const defaults = {};
442
+ for (const s of seeds)
443
+ defaults[s.key] = s.default;
444
+ return defaults;
445
+ });
446
+ })
447
+ .catch(() => { });
448
+ }, [hydrated]);
426
449
  // --- Detect modules ---
427
450
  useEffect(() => {
428
451
  const declaredKeys = new Set((declaredModules ?? []).map(m => m.key));
@@ -777,7 +800,7 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
777
800
  fontSize: 12, backgroundColor: '#f0f9ff', color: '#0369a1',
778
801
  border: '1px solid #bae6fd', borderRadius: 16, padding: '4px 10px',
779
802
  }, children: [mod.icon, " ", mod.label] }, key)) : null;
780
- }) })] }), _jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.summary.seedTitle') }), _jsx("p", { style: { ...S.summaryText, marginBottom: 12 }, children: t('setup.summary.seedInfo') }), _jsxs("div", { style: S.checkRow, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: seedOptions.activities, onChange: e => setSeedOptions({ ...seedOptions, activities: e.target.checked, demoData: e.target.checked ? seedOptions.demoData : false }), disabled: installing || !!installResult?.ok }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.summary.seedActivities') }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af' }, children: t('setup.summary.seedActivitiesDesc') })] })] }), _jsxs("div", { style: S.checkRow, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: seedOptions.demoUsers, onChange: e => setSeedOptions({ ...seedOptions, demoUsers: e.target.checked }), disabled: installing || !!installResult?.ok }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.summary.seedDemoUsers') }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af' }, children: t('setup.summary.seedDemoUsersDesc') })] })] }), _jsxs("div", { style: S.checkRow, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: seedOptions.demoData, onChange: e => setSeedOptions({ ...seedOptions, demoData: e.target.checked, activities: e.target.checked ? true : seedOptions.activities }), disabled: installing || !!installResult?.ok }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: t('setup.summary.seedDemoData') }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af' }, children: t('setup.summary.seedDemoDataDesc') })] })] })] }), installResult && (_jsx("div", { style: S.alert(installResult.ok ? 'success' : 'error'), children: installResult.ok ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: { fontWeight: 600, marginBottom: 4 }, children: ["\u2705 ", t('setup.summary.success')] }), _jsx("div", { children: t('setup.summary.successDesc') }), installResult.needsRestart && (_jsx("div", { style: { color: '#92400e', fontWeight: 500, marginTop: 8 }, children: t('setup.summary.needsRestart') }))] })) : (_jsxs("div", { children: ["\u274C ", installResult.error] })) })), installResult?.ok && (_jsxs("div", { style: { ...S.summaryCard, marginTop: 16 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }, children: [_jsx("span", { style: { fontSize: 18 }, children: "\uD83D\uDD0C" }), _jsx("div", { style: S.summaryTitle, children: "Cablage des modules" })] }), _jsx("div", { style: { fontSize: 13, color: '#6b7280', marginBottom: 12 }, children: "Cablez les modules pour injecter schemas, routes API, pages et permissions dans l'application." }), wireMessage && (_jsx("div", { style: S.alert(wireMessage.type), children: wireMessage.text })), wireLoading ? (_jsx("div", { style: { textAlign: 'center', padding: 12, color: '#6b7280' }, children: "Chargement..." })) : wireModules.length === 0 ? (_jsx("div", { style: { textAlign: 'center', padding: 12, color: '#9ca3af' }, children: "Aucun manifeste de cablage trouve" })) : (_jsx("div", { style: S.wireGrid, children: wireModules.map((mod) => (_jsxs("div", { style: S.wireCard(mod.installed), children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: S.wireStatus(mod.installed) }), _jsxs("span", { style: { fontWeight: 700, fontSize: 14 }, children: ["@mostajs/", mod.name] })] }), _jsx("span", { style: {
803
+ }) })] }), availableSeeds.length > 0 && (_jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.summary.seedTitle') }), _jsx("p", { style: { ...S.summaryText, marginBottom: 12 }, children: t('setup.summary.seedInfo') }), availableSeeds.map(seed => (_jsxs("div", { style: S.checkRow, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: seedOptions[seed.key] ?? false, onChange: e => setSeedOptions({ ...seedOptions, [seed.key]: e.target.checked }), disabled: installing || !!installResult?.ok }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: seed.label }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af' }, children: seed.description })] })] }, seed.key)))] })), installResult && (_jsx("div", { style: S.alert(installResult.ok ? 'success' : 'error'), children: installResult.ok ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: { fontWeight: 600, marginBottom: 4 }, children: ["\u2705 ", t('setup.summary.success')] }), _jsx("div", { children: t('setup.summary.successDesc') }), installResult.needsRestart && (_jsx("div", { style: { color: '#92400e', fontWeight: 500, marginTop: 8 }, children: t('setup.summary.needsRestart') }))] })) : (_jsxs("div", { children: ["\u274C ", installResult.error] })) })), installResult?.ok && (_jsxs("div", { style: { ...S.summaryCard, marginTop: 16 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }, children: [_jsx("span", { style: { fontSize: 18 }, children: "\uD83D\uDD0C" }), _jsx("div", { style: S.summaryTitle, children: "Cablage des modules" })] }), _jsx("div", { style: { fontSize: 13, color: '#6b7280', marginBottom: 12 }, children: "Cablez les modules pour injecter schemas, routes API, pages et permissions dans l'application." }), wireMessage && (_jsx("div", { style: S.alert(wireMessage.type), children: wireMessage.text })), wireLoading ? (_jsx("div", { style: { textAlign: 'center', padding: 12, color: '#6b7280' }, children: "Chargement..." })) : wireModules.length === 0 ? (_jsx("div", { style: { textAlign: 'center', padding: 12, color: '#9ca3af' }, children: "Aucun manifeste de cablage trouve" })) : (_jsx("div", { style: S.wireGrid, children: wireModules.map((mod) => (_jsxs("div", { style: S.wireCard(mod.installed), children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: S.wireStatus(mod.installed) }), _jsxs("span", { style: { fontWeight: 700, fontSize: 14 }, children: ["@mostajs/", mod.name] })] }), _jsx("span", { style: {
781
804
  fontSize: 10, fontWeight: 600, padding: '2px 6px', borderRadius: 4,
782
805
  backgroundColor: mod.type === 'business' ? '#dbeafe' : '#f3e8ff',
783
806
  color: mod.type === 'business' ? '#1e40af' : '#6b21a8',
@@ -35,6 +35,11 @@ export interface SetupJsonSeed {
35
35
  hashField?: string;
36
36
  roleField?: string;
37
37
  defaults?: Record<string, unknown>;
38
+ lookupFields?: Record<string, {
39
+ collection: string;
40
+ match: string;
41
+ value: string;
42
+ }>;
38
43
  data: Record<string, unknown>[];
39
44
  }
40
45
  export interface SetupJson {
@@ -134,8 +134,18 @@ function buildSeedDefinition(seedDef, repoFactory) {
134
134
  run: async () => {
135
135
  const getRepo = repoFactory ?? defaultRepoFactory;
136
136
  const repo = await getRepo(seedDef.collection);
137
+ // Resolve lookupFields once (e.g. createdBy → user where email = admin@test.dz → userId)
138
+ const resolved = {};
139
+ if (seedDef.lookupFields) {
140
+ for (const [field, lookup] of Object.entries(seedDef.lookupFields)) {
141
+ const lookupRepo = await getRepo(lookup.collection);
142
+ const found = await lookupRepo.findOne({ [lookup.match]: lookup.value });
143
+ if (found)
144
+ resolved[field] = found.id;
145
+ }
146
+ }
137
147
  for (const rawItem of seedDef.data) {
138
- const item = { ...(seedDef.defaults ?? {}), ...rawItem };
148
+ const item = { ...(seedDef.defaults ?? {}), ...resolved, ...rawItem };
139
149
  // Hash field if configured (e.g. password)
140
150
  if (seedDef.hashField && item[seedDef.hashField]) {
141
151
  const bcryptModule = await import('bcryptjs');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/setup",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
4
4
  "description": "Reusable setup wizard module — multi-dialect DB configuration, .env.local writer, seed runner",
5
5
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
6
  "license": "MIT",