@mostajs/setup 1.4.2 → 1.4.3

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.
@@ -12,7 +12,7 @@ export function createTestDbHandler(needsSetup) {
12
12
  return Response.json({ error: 'Already installed' }, { status: 400 });
13
13
  }
14
14
  const body = await req.json();
15
- const { dialect, host, port, name, user, password } = body;
15
+ const { dialect, host, port, name, user, password, createIfNotExists } = body;
16
16
  if (!dialect || !name) {
17
17
  return Response.json({ error: 'Missing required fields' }, { status: 400 });
18
18
  }
@@ -23,6 +23,7 @@ export function createTestDbHandler(needsSetup) {
23
23
  name,
24
24
  user: user || '',
25
25
  password: password || '',
26
+ createIfNotExists: !!createIfNotExists,
26
27
  });
27
28
  return Response.json(result);
28
29
  }
@@ -70,24 +70,67 @@ export function createUploadJarHandlers() {
70
70
  }
71
71
  }
72
72
  /**
73
- * PATCH — Start/test the JDBC bridge for a given dialect.
74
- * Body: { dialect: string, uri: string }
73
+ * PATCH — Start or stop the JDBC bridge.
74
+ * Start: { action: 'start', dialect: string, host: string, port: number, name: string, user?: string, password?: string }
75
+ * Stop: { action: 'stop', port: number }
75
76
  * Returns: { ok: true, port: number } or { ok: false, error: string }
76
77
  */
77
78
  async function PATCH(req) {
78
79
  try {
79
- const { dialect, uri } = await req.json();
80
- if (!dialect || !uri) {
81
- return Response.json({ ok: false, error: 'dialect et uri requis' }, { status: 400 });
80
+ const body = await req.json();
81
+ const { action } = body;
82
+ if (action === 'stop') {
83
+ const bridgePort = body.port || 8765;
84
+ // Kill bridge on the given port
85
+ try {
86
+ const { BridgeManager } = await import('@mostajs/orm');
87
+ const manager = BridgeManager.getInstance();
88
+ const bridges = manager.list();
89
+ const bridge = bridges.find((b) => b.port === bridgePort);
90
+ if (bridge) {
91
+ await manager.stop(bridge.key);
92
+ return Response.json({ ok: true, message: `Bridge arrete sur le port ${bridgePort}` });
93
+ }
94
+ // Fallback: kill process on port
95
+ const { execSync } = await import('child_process');
96
+ try {
97
+ execSync(`fuser -k ${bridgePort}/tcp 2>/dev/null`, { stdio: 'ignore' });
98
+ }
99
+ catch { /* port may already be free */ }
100
+ return Response.json({ ok: true, message: `Port ${bridgePort} libere` });
101
+ }
102
+ catch (err) {
103
+ const msg = err instanceof Error ? err.message : 'Erreur';
104
+ return Response.json({ ok: false, error: msg });
105
+ }
82
106
  }
83
- const { testConnection } = await import('@mostajs/orm');
84
- const result = await testConnection({ dialect, uri, schemaStrategy: 'none' });
85
- if (result.ok) {
86
- return Response.json({ ok: true });
107
+ // Default: action === 'start'
108
+ const { dialect, host, port, name, user, password } = body;
109
+ if (!dialect) {
110
+ return Response.json({ ok: false, error: 'dialect requis' }, { status: 400 });
87
111
  }
88
- else {
89
- return Response.json({ ok: false, error: result.error || 'Echec lancement du bridge' });
112
+ // Compose URI using setup's composeDbUri for consistency with test-db
113
+ const { composeDbUri } = await import('../lib/compose-uri');
114
+ const uri = composeDbUri(dialect, {
115
+ host: host || 'localhost',
116
+ port: port || 0,
117
+ name: name || '',
118
+ user: user || '',
119
+ password: password || '',
120
+ });
121
+ // Start bridge via BridgeManager directly (no SELECT 1 — just start the Java process)
122
+ const { BridgeManager } = await import('@mostajs/orm');
123
+ const { JdbcNormalizer } = await import('@mostajs/orm');
124
+ const manager = BridgeManager.getInstance();
125
+ // Check if JAR is available
126
+ if (!JdbcNormalizer.isAvailable(dialect)) {
127
+ return Response.json({
128
+ ok: false,
129
+ error: `Aucun JAR JDBC trouve pour ${dialect}. Uploadez le JAR d'abord.`,
130
+ });
90
131
  }
132
+ const bridge = await manager.getOrCreate(dialect, uri);
133
+ return Response.json({ ok: true, port: bridge.port });
91
134
  }
92
135
  catch (err) {
93
136
  const message = err instanceof Error ? err.message : 'Erreur serveur';
@@ -187,19 +187,6 @@ function resolveModuleDeps(selected, all) {
187
187
  }
188
188
  return Array.from(set);
189
189
  }
190
- function simpleDbUri(dialect, cfg) {
191
- if (dialect === 'sqlite')
192
- return `./data/${cfg.name}.db`;
193
- if (dialect === 'mongodb')
194
- return `mongodb://${cfg.user ? `${cfg.user}:${cfg.password}@` : ''}${cfg.host}:${cfg.port}/${cfg.name}`;
195
- const scheme = {
196
- postgres: 'postgresql', mysql: 'mysql', mariadb: 'mysql', mssql: 'mssql',
197
- oracle: 'oracle:thin', cockroachdb: 'postgresql', db2: 'db2', hana: 'sap',
198
- hsqldb: 'hsqldb:hsql', spanner: 'spanner', sybase: 'sybase:Tds',
199
- };
200
- const s = scheme[dialect] || dialect;
201
- return `${s}://${cfg.user ? `${cfg.user}:${cfg.password}@` : ''}${cfg.host}:${cfg.port}/${cfg.name}`;
202
- }
203
190
  async function safeJson(res) {
204
191
  try {
205
192
  return JSON.parse(await res.text());
@@ -229,9 +216,10 @@ async function fetchRetry(url, init, retries = 3, delay = 2000) {
229
216
  return fetch(url, init);
230
217
  }
231
218
  // ── JAR Upload Sub-component ─────────────────────────────────
232
- function JarUploadInline({ dialect, jarEndpoint, dbUri }) {
219
+ function JarUploadInline({ dialect, jarEndpoint, dbConfig }) {
233
220
  const [uploading, setUploading] = useState(false);
234
- const [bridgeTesting, setBridgeTesting] = useState(false);
221
+ const [bridgeLoading, setBridgeLoading] = useState(false);
222
+ const [bridgePort, setBridgePort] = useState(null);
235
223
  const [message, setMessage] = useState(null);
236
224
  const [jarStatus, setJarStatus] = useState(null);
237
225
  useEffect(() => {
@@ -273,21 +261,18 @@ function JarUploadInline({ dialect, jarEndpoint, dbUri }) {
273
261
  }
274
262
  };
275
263
  const handleStartBridge = async () => {
276
- if (!dbUri) {
277
- setMessage({ ok: false, text: 'Renseignez les parametres de connexion avant de lancer le bridge' });
278
- return;
279
- }
280
- setBridgeTesting(true);
264
+ setBridgeLoading(true);
281
265
  setMessage(null);
282
266
  try {
283
267
  const res = await fetch(jarEndpoint, {
284
268
  method: 'PATCH',
285
269
  headers: { 'Content-Type': 'application/json' },
286
- body: JSON.stringify({ dialect, uri: dbUri }),
270
+ body: JSON.stringify({ action: 'start', dialect, ...dbConfig }),
287
271
  });
288
272
  const result = await res.json();
289
273
  if (result.ok) {
290
- setMessage({ ok: true, text: 'Bridge JDBC lance avec succes' });
274
+ setBridgePort(result.port || 8765);
275
+ setMessage({ ok: true, text: `Bridge JDBC lance sur le port ${result.port || 8765}` });
291
276
  }
292
277
  else {
293
278
  setMessage({ ok: false, text: result.error || 'Echec lancement du bridge' });
@@ -297,14 +282,35 @@ function JarUploadInline({ dialect, jarEndpoint, dbUri }) {
297
282
  setMessage({ ok: false, text: 'Erreur reseau' });
298
283
  }
299
284
  finally {
300
- setBridgeTesting(false);
285
+ setBridgeLoading(false);
286
+ }
287
+ };
288
+ const handleStopBridge = async () => {
289
+ setBridgeLoading(true);
290
+ setMessage(null);
291
+ try {
292
+ const res = await fetch(jarEndpoint, {
293
+ method: 'PATCH',
294
+ headers: { 'Content-Type': 'application/json' },
295
+ body: JSON.stringify({ action: 'stop', port: bridgePort || 8765 }),
296
+ });
297
+ const result = await res.json();
298
+ if (result.ok) {
299
+ setBridgePort(null);
300
+ setMessage({ ok: true, text: result.message || 'Bridge arrete' });
301
+ }
302
+ else {
303
+ setMessage({ ok: false, text: result.error || 'Echec arret du bridge' });
304
+ }
305
+ }
306
+ catch {
307
+ setMessage({ ok: false, text: 'Erreur reseau' });
308
+ }
309
+ finally {
310
+ setBridgeLoading(false);
301
311
  }
302
312
  };
303
- 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: { ...S.flex(12), marginTop: 8, flexWrap: 'wrap' }, 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' } })] }), jarStatus?.hasJar && (_jsx("button", { style: {
304
- ...S.btn('primary', bridgeTesting),
305
- fontSize: 12, padding: '6px 12px',
306
- backgroundColor: bridgeTesting ? '#6b7280' : '#059669',
307
- }, onClick: handleStartBridge, disabled: bridgeTesting, children: bridgeTesting ? 'Lancement...' : 'Lancer le bridge' })), _jsx("span", { style: { fontSize: 11, color: '#9ca3af' }, children: "Ex: hsqldb*.jar, ojdbc*.jar, db2jcc*.jar" })] }), message && (_jsx("p", { style: { fontSize: 12, color: message.ok ? '#059669' : '#dc2626', marginTop: 8 }, children: message.text }))] }));
313
+ 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" })), bridgePort && (_jsxs("span", { style: { fontSize: 11, color: '#059669', fontWeight: 600 }, children: ["Bridge actif port ", bridgePort] }))] }), _jsxs("div", { style: { ...S.flex(8), marginTop: 8, flexWrap: 'wrap' }, 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' } })] }), jarStatus?.hasJar && !bridgePort && (_jsx("button", { style: { ...S.btn('primary', bridgeLoading), fontSize: 12, padding: '6px 12px', backgroundColor: bridgeLoading ? '#6b7280' : '#059669' }, onClick: handleStartBridge, disabled: bridgeLoading, children: bridgeLoading ? 'Lancement...' : 'Lancer le bridge' })), bridgePort && (_jsx("button", { style: { ...S.btn('primary', bridgeLoading), fontSize: 12, padding: '6px 12px', backgroundColor: bridgeLoading ? '#6b7280' : '#dc2626' }, onClick: handleStopBridge, disabled: bridgeLoading, children: bridgeLoading ? 'Arret...' : 'Arreter le bridge' })), _jsx("span", { style: { fontSize: 11, color: '#9ca3af' }, children: "Ex: hsqldb*.jar, ojdbc*.jar, db2jcc*.jar" })] }), message && (_jsx("p", { style: { fontSize: 12, color: message.ok ? '#059669' : '#dc2626', marginTop: 8 }, children: message.text }))] }));
308
314
  }
309
315
  // ── Main Component ───────────────────────────────────────────
310
316
  export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNamePrefix = 'mydb', persistState = true, }) {
@@ -328,6 +334,7 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
328
334
  const [selectedModules, setSelectedModules] = useState([]);
329
335
  const [detectedModules, setDetectedModules] = useState([]);
330
336
  const [modulesDetected, setModulesDetected] = useState(false);
337
+ const [createIfNotExists, setCreateIfNotExists] = useState(true);
331
338
  const [installing, setInstalling] = useState(false);
332
339
  const [installResult, setInstallResult] = useState(null);
333
340
  const [hydrated, setHydrated] = useState(false);
@@ -431,7 +438,7 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
431
438
  const res = await fetch(ep.testDb, {
432
439
  method: 'POST',
433
440
  headers: { 'Content-Type': 'application/json' },
434
- body: JSON.stringify({ dialect, ...dbConfig }),
441
+ body: JSON.stringify({ dialect, ...dbConfig, createIfNotExists }),
435
442
  });
436
443
  const data = await res.json();
437
444
  if (data.ok) {
@@ -530,7 +537,7 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
530
537
  const isSelected = selectedModules.includes(mod.key);
531
538
  const isDetected = detectedModules.includes(mod.key);
532
539
  return (_jsxs("div", { style: S.moduleCard(isSelected, !!mod.required), onClick: () => toggleModule(mod.key), children: [_jsxs("div", { style: S.moduleHeader, children: [_jsxs("div", { style: S.moduleLeft, children: [_jsx("span", { style: { fontSize: 20 }, children: mod.icon }), _jsx("span", { style: S.moduleName, children: mod.label })] }), _jsxs("div", { style: S.moduleBadges, children: [mod.discovered && _jsx("span", { style: S.badge('new'), children: "Nouveau" }), isDetected && _jsx("span", { style: S.badge('installed'), children: t('setup.modules.installed') }), mod.required && _jsx("span", { style: S.badge('required'), children: t('setup.modules.required') }), _jsx("input", { type: "checkbox", checked: isSelected, disabled: mod.required, readOnly: true, style: S.checkbox })] })] }), _jsx("div", { style: S.moduleDesc, children: mod.description }), mod.dependsOn?.length ? (_jsxs("div", { style: { fontSize: 11, color: '#9ca3af', marginTop: 4 }, children: ["Depend de : ", mod.dependsOn.join(', ')] })) : null] }, mod.key));
533
- }) }), _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); } })] }), dialect !== 'hsqldb' && (_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); } })] }), _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 !== '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, dbUri: simpleDbUri(dialect, dbConfig) })), 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 && (_jsx("span", { style: { fontSize: 13, color: dbTestResult.ok ? '#059669' : '#dc2626' }, children: dbTestResult.ok
540
+ }) }), _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); } })] }), dialect !== 'hsqldb' && (_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); } })] }), _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 !== '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 && (_jsx("span", { style: { fontSize: 13, color: dbTestResult.ok ? '#059669' : '#dc2626' }, children: dbTestResult.ok
534
541
  ? `✅ ${t('setup.database.success')}${dbTestResult.dbVersion ? ` (v${dbTestResult.dbVersion})` : ''}`
535
542
  : `❌ ${t('setup.database.error')}: ${dbTestResult.error}` }))] })), _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 => {
536
543
  const mod = availableModules.find(m => m.key === key);
@@ -4,6 +4,7 @@ import type { DialectType, DbConfig } from '../types/index';
4
4
  */
5
5
  export declare function testDbConnection(params: {
6
6
  dialect: DialectType;
7
+ createIfNotExists?: boolean;
7
8
  } & DbConfig): Promise<{
8
9
  ok: boolean;
9
10
  error?: string;
@@ -3,7 +3,7 @@ import { composeDbUri } from './compose-uri';
3
3
  * Test a database connection without affecting the global dialect singleton.
4
4
  */
5
5
  export async function testDbConnection(params) {
6
- const { dialect, ...dbConfig } = params;
6
+ const { dialect, createIfNotExists, ...dbConfig } = params;
7
7
  try {
8
8
  switch (dialect) {
9
9
  case 'mongodb': {
@@ -26,7 +26,11 @@ export async function testDbConnection(params) {
26
26
  default: {
27
27
  const uri = composeDbUri(dialect, dbConfig);
28
28
  const { testConnection } = await import('@mostajs/orm');
29
- const result = await testConnection({ dialect, uri, schemaStrategy: 'none' });
29
+ const result = await testConnection({
30
+ dialect,
31
+ uri,
32
+ schemaStrategy: createIfNotExists ? 'update' : 'none',
33
+ });
30
34
  return result;
31
35
  }
32
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/setup",
3
- "version": "1.4.2",
3
+ "version": "1.4.3",
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",