@mostajs/setup 1.4.5 → 1.4.7

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.
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Creates handlers for JAR file management API.
3
3
  *
4
- * GET — list uploaded JARs and JDBC dialect status
4
+ * GET — list uploaded JARs, JDBC dialect status, active bridges, HSQLDB server status
5
5
  * POST — upload a new JAR file (multipart/form-data)
6
6
  * DELETE — remove a JAR file
7
+ * PATCH — start/stop HSQLDB server and JDBC bridge
7
8
  */
8
9
  export declare function createUploadJarHandlers(): {
9
10
  GET: () => Promise<Response>;
@@ -1,20 +1,49 @@
1
1
  // @mostajs/setup — API Route factory for JAR file upload/list/delete
2
2
  // Delegates to @mostajs/orm jar-upload module
3
3
  // Author: Dr Hamid MADANI drmdh@msn.com
4
+ /**
5
+ * Check if a TCP port is open (non-HTTP — raw socket connect).
6
+ */
7
+ async function isPortOpen(port, host = 'localhost', timeoutMs = 800) {
8
+ const net = await import('net');
9
+ return new Promise(resolve => {
10
+ const socket = new net.Socket();
11
+ socket.setTimeout(timeoutMs);
12
+ socket.once('connect', () => { socket.destroy(); resolve(true); });
13
+ socket.once('timeout', () => { socket.destroy(); resolve(false); });
14
+ socket.once('error', () => { socket.destroy(); resolve(false); });
15
+ socket.connect(port, host);
16
+ });
17
+ }
18
+ /**
19
+ * Get PID of process listening on a port via fuser.
20
+ */
21
+ async function getPidOnPort(port) {
22
+ try {
23
+ const { execSync } = await import('child_process');
24
+ const out = execSync(`fuser ${port}/tcp 2>/dev/null`, { encoding: 'utf-8' }).trim();
25
+ const pid = parseInt(out);
26
+ return isNaN(pid) ? 0 : pid;
27
+ }
28
+ catch {
29
+ return 0;
30
+ }
31
+ }
4
32
  /**
5
33
  * Creates handlers for JAR file management API.
6
34
  *
7
- * GET — list uploaded JARs and JDBC dialect status
35
+ * GET — list uploaded JARs, JDBC dialect status, active bridges, HSQLDB server status
8
36
  * POST — upload a new JAR file (multipart/form-data)
9
37
  * DELETE — remove a JAR file
38
+ * PATCH — start/stop HSQLDB server and JDBC bridge
10
39
  */
11
40
  export function createUploadJarHandlers() {
12
41
  async function GET() {
13
42
  try {
14
43
  const { listJarFiles, getJdbcDialectStatus } = await import('@mostajs/orm');
15
- // Scan active bridges: BridgeManager bridges + PID files + port probing
44
+ // Scan active bridges
16
45
  const bridges = [];
17
- // 1. Check BridgeManager known bridges
46
+ // 1. BridgeManager known bridges
18
47
  try {
19
48
  const { BridgeManager } = await import('@mostajs/orm');
20
49
  const manager = BridgeManager.getInstance();
@@ -23,7 +52,7 @@ export function createUploadJarHandlers() {
23
52
  }
24
53
  }
25
54
  catch { /* ignore */ }
26
- // 2. Scan PID files for orphan bridges
55
+ // 2. PID files for orphan bridges
27
56
  try {
28
57
  const { existsSync, readdirSync, readFileSync } = await import('fs');
29
58
  const { join } = await import('path');
@@ -35,14 +64,12 @@ export function createUploadJarHandlers() {
35
64
  if (!portMatch)
36
65
  continue;
37
66
  const port = parseInt(portMatch[1]);
38
- // Skip if already known from BridgeManager
39
67
  if (bridges.some(b => b.port === port))
40
68
  continue;
41
69
  const pidStr = readFileSync(join(jarDir, file), 'utf-8').trim();
42
70
  const pid = parseInt(pidStr);
43
71
  if (isNaN(pid))
44
72
  continue;
45
- // Check if process is alive
46
73
  let alive = false;
47
74
  try {
48
75
  process.kill(pid, 0);
@@ -50,23 +77,28 @@ export function createUploadJarHandlers() {
50
77
  }
51
78
  catch { /* dead */ }
52
79
  if (alive) {
53
- // Probe health
54
80
  let jdbcUrl;
55
81
  try {
56
82
  const res = await fetch(`http://localhost:${port}/health`, { signal: AbortSignal.timeout(1000) });
57
83
  if (res.ok) {
58
- const h = await res.json();
59
- jdbcUrl = h.jdbcUrl;
84
+ jdbcUrl = (await res.json()).jdbcUrl;
60
85
  }
61
86
  }
62
87
  catch { /* not responding */ }
63
88
  bridges.push({ port, pid, status: jdbcUrl ? 'active' : 'orphan', jdbcUrl });
64
89
  }
90
+ else {
91
+ // Clean stale PID file
92
+ try {
93
+ (await import('fs')).unlinkSync(join(jarDir, file));
94
+ }
95
+ catch { /* ignore */ }
96
+ }
65
97
  }
66
98
  }
67
99
  }
68
100
  catch { /* ignore */ }
69
- // 3. Probe common bridge ports for unknown bridges (no PID file)
101
+ // 3. Probe common bridge ports (HTTP bridges speak HTTP)
70
102
  const basePort = parseInt(process.env.MOSTA_BRIDGE_PORT_BASE || '8765');
71
103
  for (let port = basePort; port < basePort + 5; port++) {
72
104
  if (bridges.some(b => b.port === port))
@@ -74,17 +106,64 @@ export function createUploadJarHandlers() {
74
106
  try {
75
107
  const res = await fetch(`http://localhost:${port}/health`, { signal: AbortSignal.timeout(500) });
76
108
  if (res.ok) {
77
- const h = await res.json();
78
- bridges.push({ port, pid: 0, status: 'active', jdbcUrl: h.jdbcUrl });
109
+ const h = (await res.json());
110
+ const pid = await getPidOnPort(port);
111
+ bridges.push({ port, pid, status: 'active', jdbcUrl: h.jdbcUrl });
79
112
  }
80
113
  }
81
114
  catch { /* not a bridge */ }
82
115
  }
116
+ // Check HSQLDB server status (TCP probe, NOT HTTP)
117
+ let hsqldbServer = null;
118
+ try {
119
+ const { existsSync, readdirSync, readFileSync } = await import('fs');
120
+ const { join } = await import('path');
121
+ const jarDir = process.env.MOSTA_JAR_DIR || join(process.cwd(), 'jar_files');
122
+ // Check PID files first
123
+ if (existsSync(jarDir)) {
124
+ const serverPidFiles = readdirSync(jarDir).filter((f) => f.startsWith('.hsqldb-server-') && f.endsWith('.pid'));
125
+ for (const file of serverPidFiles) {
126
+ const portMatch = file.match(/\.hsqldb-server-(\d+)\.pid/);
127
+ if (!portMatch)
128
+ continue;
129
+ const port = parseInt(portMatch[1]);
130
+ const pidStr = readFileSync(join(jarDir, file), 'utf-8').trim();
131
+ const pid = parseInt(pidStr);
132
+ let alive = false;
133
+ if (pid > 0) {
134
+ try {
135
+ process.kill(pid, 0);
136
+ alive = true;
137
+ }
138
+ catch { /* dead */ }
139
+ }
140
+ if (alive) {
141
+ hsqldbServer = { running: true, port, pid };
142
+ }
143
+ else {
144
+ try {
145
+ (await import('fs')).unlinkSync(join(jarDir, file));
146
+ }
147
+ catch { /* ignore */ }
148
+ }
149
+ }
150
+ }
151
+ // Fallback: check port 9001 via TCP
152
+ if (!hsqldbServer) {
153
+ const open = await isPortOpen(9001);
154
+ if (open) {
155
+ const pid = await getPidOnPort(9001);
156
+ hsqldbServer = { running: true, port: 9001, pid };
157
+ }
158
+ }
159
+ }
160
+ catch { /* ignore */ }
83
161
  return Response.json({
84
162
  ok: true,
85
163
  jars: listJarFiles(),
86
164
  dialects: getJdbcDialectStatus(),
87
165
  bridges,
166
+ hsqldbServer,
88
167
  });
89
168
  }
90
169
  catch (err) {
@@ -139,40 +218,144 @@ export function createUploadJarHandlers() {
139
218
  }
140
219
  }
141
220
  /**
142
- * PATCH — Start or stop the JDBC bridge.
143
- * Start: { action: 'start', dialect: string, host: string, port: number, name: string, user?: string, password?: string }
144
- * Stop: { action: 'stop', port: number }
145
- * Returns: { ok: true, port: number } or { ok: false, error: string }
221
+ * PATCH — Manage JDBC bridge and HSQLDB server.
222
+ * start-server: { action: 'start-server', dialect, name, host?, port? }
223
+ * stop-server: { action: 'stop-server', port? }
224
+ * start: { action: 'start', dialect, host, port, name, user?, password? }
225
+ * stop: { action: 'stop', port, pid? }
146
226
  */
147
227
  async function PATCH(req) {
148
228
  try {
149
229
  const body = await req.json();
150
230
  const { action } = body;
231
+ // ── Start HSQLDB server ──
232
+ if (action === 'start-server') {
233
+ const { dialect, name, host, port: sgbdPort } = body;
234
+ if (dialect !== 'hsqldb') {
235
+ return Response.json({ ok: false, error: 'start-server supporte uniquement hsqldb' }, { status: 400 });
236
+ }
237
+ const actualPort = sgbdPort || 9001;
238
+ // Check if already running (TCP probe, not HTTP)
239
+ if (await isPortOpen(actualPort, host || 'localhost')) {
240
+ const pid = await getPidOnPort(actualPort);
241
+ return Response.json({ ok: true, message: `Serveur HSQLDB deja en marche sur le port ${actualPort}`, port: actualPort, pid, alreadyRunning: true });
242
+ }
243
+ // Find JAR
244
+ const { JdbcNormalizer } = await import('@mostajs/orm');
245
+ const jarPath = JdbcNormalizer.findJar('hsqldb');
246
+ if (!jarPath) {
247
+ return Response.json({ ok: false, error: 'JAR HSQLDB non trouve. Uploadez hsqldb*.jar d\'abord.' });
248
+ }
249
+ // Launch server
250
+ const { spawn: spawnChild } = await import('child_process');
251
+ const { join } = await import('path');
252
+ const { writeFileSync, existsSync, mkdirSync } = await import('fs');
253
+ const dataDir = join(process.cwd(), 'data');
254
+ if (!existsSync(dataDir))
255
+ mkdirSync(dataDir, { recursive: true });
256
+ const dbAlias = name || 'mydb';
257
+ const serverProc = spawnChild('java', [
258
+ '-cp', jarPath,
259
+ 'org.hsqldb.server.Server',
260
+ '--database.0', `file:${join(dataDir, dbAlias)}`,
261
+ '--dbname.0', dbAlias,
262
+ '--port', String(actualPort),
263
+ ], {
264
+ stdio: ['ignore', 'pipe', 'pipe'],
265
+ detached: true,
266
+ });
267
+ serverProc.unref();
268
+ const serverPid = serverProc.pid || 0;
269
+ // Save PID file
270
+ const jarDir = process.env.MOSTA_JAR_DIR || join(process.cwd(), 'jar_files');
271
+ if (existsSync(jarDir)) {
272
+ writeFileSync(join(jarDir, `.hsqldb-server-${actualPort}.pid`), String(serverPid));
273
+ }
274
+ // Log stderr
275
+ serverProc.stderr?.on('data', (data) => {
276
+ const msg = data.toString().trim();
277
+ if (msg)
278
+ console.error(`[HSQLDB:server] ${msg}`);
279
+ });
280
+ // Wait for server to be ready (TCP probe)
281
+ let serverReady = false;
282
+ for (let i = 0; i < 16; i++) {
283
+ await new Promise(r => setTimeout(r, 500));
284
+ if (await isPortOpen(actualPort, host || 'localhost')) {
285
+ serverReady = true;
286
+ break;
287
+ }
288
+ // Also check process is still alive
289
+ try {
290
+ process.kill(serverPid, 0);
291
+ }
292
+ catch {
293
+ break; /* process died */
294
+ }
295
+ }
296
+ if (!serverReady) {
297
+ return Response.json({ ok: false, error: 'Le serveur HSQLDB n\'a pas demarre dans le delai imparti' });
298
+ }
299
+ return Response.json({ ok: true, port: actualPort, pid: serverPid, message: `Serveur HSQLDB lance (port ${actualPort}, PID ${serverPid}, alias: ${dbAlias})` });
300
+ }
301
+ // ── Stop HSQLDB server ──
302
+ if (action === 'stop-server') {
303
+ const sgbdPort = body.port || 9001;
304
+ try {
305
+ const { execSync } = await import('child_process');
306
+ const { existsSync, unlinkSync, readFileSync } = await import('fs');
307
+ const { join } = await import('path');
308
+ const jarDir = process.env.MOSTA_JAR_DIR || join(process.cwd(), 'jar_files');
309
+ // Kill by PID file
310
+ const pidFile = join(jarDir, `.hsqldb-server-${sgbdPort}.pid`);
311
+ if (existsSync(pidFile)) {
312
+ const pid = parseInt(readFileSync(pidFile, 'utf-8').trim());
313
+ if (pid > 0) {
314
+ try {
315
+ process.kill(pid, 'SIGKILL');
316
+ }
317
+ catch { /* dead */ }
318
+ }
319
+ unlinkSync(pidFile);
320
+ }
321
+ // Also kill anything on the port
322
+ try {
323
+ execSync(`fuser -k ${sgbdPort}/tcp 2>/dev/null`, { stdio: 'ignore' });
324
+ }
325
+ catch { /* already free */ }
326
+ return Response.json({ ok: true, message: `Serveur HSQLDB arrete (port ${sgbdPort})` });
327
+ }
328
+ catch (err) {
329
+ return Response.json({ ok: false, error: err instanceof Error ? err.message : 'Erreur' });
330
+ }
331
+ }
332
+ // ── Stop bridge ──
151
333
  if (action === 'stop') {
152
334
  const bridgePort = body.port || 8765;
153
335
  const bridgePid = body.pid || 0;
154
336
  try {
155
337
  // 1. Try BridgeManager
156
- const { BridgeManager } = await import('@mostajs/orm');
157
- const manager = BridgeManager.getInstance();
158
- const bridges = manager.list();
159
- const bridge = bridges.find((b) => b.port === bridgePort);
160
- if (bridge) {
161
- await manager.stop(bridge.key);
338
+ try {
339
+ const { BridgeManager } = await import('@mostajs/orm');
340
+ const manager = BridgeManager.getInstance();
341
+ const bridge = manager.list().find((b) => b.port === bridgePort);
342
+ if (bridge)
343
+ await manager.stop(bridge.key);
162
344
  }
163
- // 2. Kill by PID if provided
345
+ catch { /* ignore */ }
346
+ // 2. Kill by PID
164
347
  if (bridgePid > 0) {
165
348
  try {
166
349
  process.kill(bridgePid, 'SIGKILL');
167
350
  }
168
- catch { /* already dead */ }
351
+ catch { /* dead */ }
169
352
  }
170
- // 3. Fallback: kill process on port
171
- const { execSync } = await import('child_process');
353
+ // 3. Fallback: fuser
172
354
  try {
355
+ const { execSync } = await import('child_process');
173
356
  execSync(`fuser -k ${bridgePort}/tcp 2>/dev/null`, { stdio: 'ignore' });
174
357
  }
175
- catch { /* port may already be free */ }
358
+ catch { /* already free */ }
176
359
  // 4. Clean PID file
177
360
  try {
178
361
  const { existsSync, unlinkSync } = await import('fs');
@@ -185,16 +368,14 @@ export function createUploadJarHandlers() {
185
368
  return Response.json({ ok: true, message: `Bridge arrete (port ${bridgePort})` });
186
369
  }
187
370
  catch (err) {
188
- const msg = err instanceof Error ? err.message : 'Erreur';
189
- return Response.json({ ok: false, error: msg });
371
+ return Response.json({ ok: false, error: err instanceof Error ? err.message : 'Erreur' });
190
372
  }
191
373
  }
192
- // Default: action === 'start'
374
+ // ── Start bridge ──
193
375
  const { dialect, host, port, name, user, password } = body;
194
376
  if (!dialect) {
195
377
  return Response.json({ ok: false, error: 'dialect requis' }, { status: 400 });
196
378
  }
197
- // Compose URI using setup's composeDbUri for consistency with test-db
198
379
  const { composeDbUri } = await import('../lib/compose-uri');
199
380
  const uri = composeDbUri(dialect, {
200
381
  host: host || 'localhost',
@@ -203,11 +384,9 @@ export function createUploadJarHandlers() {
203
384
  user: user || '',
204
385
  password: password || '',
205
386
  });
206
- // Start bridge via BridgeManager directly (no SELECT 1 — just start the Java process)
207
387
  const { BridgeManager } = await import('@mostajs/orm');
208
388
  const { JdbcNormalizer } = await import('@mostajs/orm');
209
389
  const manager = BridgeManager.getInstance();
210
- // Check if JAR is available
211
390
  if (!JdbcNormalizer.isAvailable(dialect)) {
212
391
  return Response.json({
213
392
  ok: false,
@@ -217,12 +217,12 @@ async function fetchRetry(url, init, retries = 3, delay = 2000) {
217
217
  }
218
218
  function JarUploadInline({ dialect, jarEndpoint, dbConfig }) {
219
219
  const [uploading, setUploading] = useState(false);
220
- const [bridgeLoading, setBridgeLoading] = useState(false);
220
+ const [loading, setLoading] = useState(null); // tracks which action is loading
221
221
  const [bridgePort, setBridgePort] = useState(null);
222
222
  const [message, setMessage] = useState(null);
223
223
  const [jarStatus, setJarStatus] = useState(null);
224
224
  const [bridges, setBridges] = useState([]);
225
- const [killingPort, setKillingPort] = useState(null);
225
+ const [serverInfo, setServerInfo] = useState(null);
226
226
  const loadStatus = useCallback(() => {
227
227
  fetch(jarEndpoint)
228
228
  .then(r => r.json())
@@ -231,10 +231,15 @@ function JarUploadInline({ dialect, jarEndpoint, dbConfig }) {
231
231
  const s = data.dialects?.find((d) => d.dialect === dialect);
232
232
  setJarStatus(s || { hasJar: false, jarFile: null });
233
233
  setBridges(data.bridges || []);
234
- // If a bridge is active, track its port
234
+ if (data.hsqldbServer)
235
+ setServerInfo(data.hsqldbServer);
236
+ else
237
+ setServerInfo(null);
235
238
  const active = (data.bridges || []).find((b) => b.status === 'active');
236
239
  if (active)
237
240
  setBridgePort(active.port);
241
+ else
242
+ setBridgePort(null);
238
243
  }
239
244
  })
240
245
  .catch(() => { });
@@ -267,63 +272,42 @@ function JarUploadInline({ dialect, jarEndpoint, dbConfig }) {
267
272
  e.target.value = '';
268
273
  }
269
274
  };
270
- const handleStartBridge = async () => {
271
- setBridgeLoading(true);
272
- setMessage(null);
273
- try {
274
- const res = await fetch(jarEndpoint, {
275
- method: 'PATCH',
276
- headers: { 'Content-Type': 'application/json' },
277
- body: JSON.stringify({ action: 'start', dialect, ...dbConfig }),
278
- });
279
- const result = await res.json();
280
- if (result.ok) {
281
- setBridgePort(result.port || 8765);
282
- setMessage({ ok: true, text: `Bridge JDBC lance sur le port ${result.port || 8765}` });
283
- loadStatus();
284
- }
285
- else {
286
- setMessage({ ok: false, text: result.error || 'Echec lancement du bridge' });
287
- }
288
- }
289
- catch {
290
- setMessage({ ok: false, text: 'Erreur reseau' });
291
- }
292
- finally {
293
- setBridgeLoading(false);
294
- }
295
- };
296
- const handleStopBridge = async (port, pid) => {
297
- setKillingPort(port);
275
+ const patchAction = async (payload, actionLabel) => {
276
+ setLoading(actionLabel);
298
277
  setMessage(null);
299
278
  try {
300
279
  const res = await fetch(jarEndpoint, {
301
280
  method: 'PATCH',
302
281
  headers: { 'Content-Type': 'application/json' },
303
- body: JSON.stringify({ action: 'stop', port, pid }),
282
+ body: JSON.stringify(payload),
304
283
  });
305
284
  const result = await res.json();
306
285
  if (result.ok) {
307
- if (bridgePort === port)
286
+ setMessage({ ok: true, text: result.message || actionLabel + ' OK' });
287
+ if (result.port && payload.action === 'start')
288
+ setBridgePort(result.port);
289
+ if (payload.action === 'stop')
308
290
  setBridgePort(null);
309
- setMessage({ ok: true, text: result.message || 'Bridge arrete' });
310
- loadStatus();
311
291
  }
312
292
  else {
313
- setMessage({ ok: false, text: result.error || 'Echec arret du bridge' });
293
+ setMessage({ ok: false, text: result.error || 'Echec ' + actionLabel });
314
294
  }
295
+ loadStatus();
315
296
  }
316
297
  catch {
317
298
  setMessage({ ok: false, text: 'Erreur reseau' });
318
299
  }
319
300
  finally {
320
- setKillingPort(null);
301
+ setLoading(null);
321
302
  }
322
303
  };
323
- 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 && bridges.length === 0 && (_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' })), _jsx("span", { style: { fontSize: 11, color: '#9ca3af' }, children: "Ex: hsqldb*.jar, ojdbc*.jar, db2jcc*.jar" })] }), bridges.length > 0 && (_jsxs("div", { style: { marginTop: 8, padding: '8px 10px', backgroundColor: '#f0fdf4', borderRadius: 6, border: '1px solid #bbf7d0' }, children: [_jsx("div", { style: { fontSize: 11, fontWeight: 600, color: '#166534', marginBottom: 4 }, children: "Bridges actifs" }), 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: [":", b.port, " ", b.pid > 0 ? `(PID ${b.pid})` : '', " ", b.jdbcUrl ? `— ${b.jdbcUrl}` : ''] }), _jsx("button", { style: {
324
- ...S.btn('primary', killingPort === b.port), fontSize: 11, padding: '2px 8px',
325
- backgroundColor: killingPort === b.port ? '#6b7280' : '#dc2626', marginLeft: 'auto',
326
- }, onClick: () => handleStopBridge(b.port, b.pid), disabled: killingPort === b.port, children: killingPort === b.port ? '...' : 'Kill' })] }, b.port))), bridges.length === 0 || bridges.some(b => b.status === 'active') ? null : (_jsx("button", { style: { ...S.btn('primary', bridgeLoading), fontSize: 12, padding: '6px 12px', backgroundColor: '#059669', marginTop: 6 }, onClick: handleStartBridge, disabled: bridgeLoading, children: bridgeLoading ? 'Lancement...' : 'Lancer le bridge' }))] })), message && (_jsx("p", { style: { fontSize: 12, color: message.ok ? '#059669' : '#dc2626', marginTop: 8 }, children: message.text }))] }));
304
+ const isHsqldb = dialect === 'hsqldb';
305
+ const btnSmall = (color, disabled) => ({
306
+ ...S.btn('primary', disabled), fontSize: 12, padding: '6px 14px',
307
+ backgroundColor: disabled ? '#9ca3af' : color, cursor: disabled ? 'not-allowed' : 'pointer',
308
+ borderRadius: 6, border: 'none', color: '#fff', fontWeight: 600,
309
+ });
310
+ 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", serverInfo?.running && (_jsxs("span", { style: { fontWeight: 400, color: '#059669', marginLeft: 8 }, children: ["En marche (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 }, 'start-server'), disabled: loading === 'start-server' || !!serverInfo?.running, children: loading === 'start-server' ? 'Demarrage...' : 'Demarrer le serveur' }), _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", bridgePort && _jsxs("span", { style: { fontWeight: 400, marginLeft: 8 }, children: ["Actif port ", bridgePort] }), !bridgePort && bridges.length === 0 && _jsx("span", { style: { fontWeight: 400, color: '#6b7280', 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' }) }), 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: [":", 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 }))] }));
327
311
  }
328
312
  // ── Main Component ───────────────────────────────────────────
329
313
  export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNamePrefix = 'mydb', persistState = true, }) {
@@ -550,7 +534,7 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
550
534
  const isSelected = selectedModules.includes(mod.key);
551
535
  const isDetected = detectedModules.includes(mod.key);
552
536
  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));
553
- }) }), _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
537
+ }) }), _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 && (_jsx("span", { style: { fontSize: 13, color: dbTestResult.ok ? '#059669' : '#dc2626' }, children: dbTestResult.ok
554
538
  ? `✅ ${t('setup.database.success')}${dbTestResult.dbVersion ? ` (v${dbTestResult.dbVersion})` : ''}`
555
539
  : `❌ ${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 => {
556
540
  const mod = availableModules.find(m => m.key === key);
@@ -28,6 +28,8 @@ export function composeDbUri(dialect, config) {
28
28
  case 'hana':
29
29
  return `hana://${eu}:${ep}@${host}:${port}`;
30
30
  case 'hsqldb':
31
+ if (user)
32
+ return `hsqldb:hsql://${eu}:${ep}@${host}:${port}/${name}`;
31
33
  return `hsqldb:hsql://${host}:${port}/${name}`;
32
34
  case 'spanner':
33
35
  return `spanner://projects/${name}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/setup",
3
- "version": "1.4.5",
3
+ "version": "1.4.7",
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",