@mostajs/orm-cli 0.2.1 → 0.2.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.
Files changed (2) hide show
  1. package/bin/mostajs.sh +194 -4
  2. package/package.json +1 -1
package/bin/mostajs.sh CHANGED
@@ -594,7 +594,8 @@ prompt_dialect() {
594
594
  sqlite) suggest="./data.sqlite" ;;
595
595
  postgres) suggest="postgres://user:pw@localhost:5432/app" ;;
596
596
  mysql|mariadb) suggest="mysql://user:pw@localhost:3306/app" ;;
597
- mongodb) suggest="mongodb://localhost:27017/app" ;;
597
+ # MongoDB : include ?authSource=admin (common pitfall without it)
598
+ mongodb) suggest="mongodb://devuser:devpass26@localhost:27017/app?authSource=admin" ;;
598
599
  mssql) suggest="mssql://user:pw@localhost:1433/app" ;;
599
600
  oracle) suggest="oracle://user:pw@localhost:1521/ORCLPDB" ;;
600
601
  db2) suggest="db2://user:pw@localhost:50000/app" ;;
@@ -772,6 +773,46 @@ function stripScheme(uri) {
772
773
  return uri;
773
774
  }
774
775
 
776
+ // Provide dialect-specific hints for common auth / connection errors.
777
+ function hintFor(dialect, rawUri, err) {
778
+ const msg = (err && err.message) ? err.message : '';
779
+ const code = err && (err.code ?? err.codeName);
780
+
781
+ // MongoDB code 18 : Authentication failed → missing ?authSource=admin
782
+ if (dialect === 'mongodb' && (code === 18 || /AuthenticationFailed|18/.test(msg))) {
783
+ if (!/authSource=/.test(rawUri)) {
784
+ return 'MongoDB users are usually declared in the admin DB. Add ?authSource=admin to the URI :\n'
785
+ + ' ' + (rawUri.includes('?') ? rawUri + '&authSource=admin' : rawUri + '?authSource=admin');
786
+ }
787
+ return 'Verify credentials (username / password) in the URI.';
788
+ }
789
+
790
+ // PostgreSQL common errors
791
+ if (dialect === 'postgres' || dialect === 'cockroachdb') {
792
+ if (/password authentication failed/i.test(msg)) return 'Wrong PG password. Check the URI or run: psql "' + rawUri + '" -c "SELECT 1"';
793
+ if (/ECONNREFUSED|connect ECONNREFUSED/i.test(msg)) return 'PG server not running on that host/port. Try: pg_isready -h HOST -p PORT';
794
+ if (/no pg_hba.conf entry/i.test(msg)) return 'Server rejects the connection (pg_hba.conf). Add your IP or use SSL.';
795
+ }
796
+
797
+ // MySQL common errors
798
+ if (dialect === 'mysql' || dialect === 'mariadb') {
799
+ if (/Access denied for user/i.test(msg)) return 'Wrong MySQL password. Try: mysql -u USER -p -h HOST';
800
+ if (/ECONNREFUSED/i.test(msg)) return 'MySQL not running. Try: systemctl status mysql';
801
+ }
802
+
803
+ // SQLite common errors
804
+ if (dialect === 'sqlite') {
805
+ if (/SQLITE_CANTOPEN/i.test(msg)) return 'Cannot open SQLite file. Check the path exists and is writable.';
806
+ }
807
+
808
+ // Generic network errors
809
+ if (/ECONNREFUSED/i.test(msg)) return 'Service is not reachable at that host/port.';
810
+ if (/ENOTFOUND|getaddrinfo/i.test(msg)) return 'DNS resolution failed. Check the hostname.';
811
+ if (/ETIMEDOUT/i.test(msg)) return 'Connection timed out. Check firewall / VPN / host.';
812
+
813
+ return null;
814
+ }
815
+
775
816
  let ok = 0, fail = 0;
776
817
  for (const [dialect, rawUri] of pairs) {
777
818
  const uri = dialect === 'sqlite' ? stripScheme(rawUri) : rawUri;
@@ -790,6 +831,9 @@ for (const [dialect, rawUri] of pairs) {
790
831
  } catch (e) {
791
832
  console.error(' \u2717 ' + (e.message ?? e));
792
833
  if (e.code) console.error(' code : ' + e.code);
834
+ if (e.codeName) console.error(' codeName : ' + e.codeName);
835
+ const hint = hintFor(dialect, rawUri, e);
836
+ if (hint) console.error(' \u2192 ' + hint);
793
837
  fail++;
794
838
  }
795
839
  }
@@ -800,7 +844,66 @@ EOF
800
844
 
801
845
  cd "$PROJECT_ROOT"
802
846
  node "$CONFIG_DIR/test-connections.mjs" "${args[@]}" 2>&1 | tee "$LOG_DIR/test-connections.log"
847
+ local test_rc=${PIPESTATUS[0]}
803
848
  echo
849
+
850
+ # Offer to auto-fix common MongoDB auth issue (missing ?authSource=admin)
851
+ if [[ $test_rc -ne 0 ]] && grep -qE "authSource=admin|AuthenticationFailed|code : 18" "$LOG_DIR/test-connections.log"; then
852
+ echo
853
+ warn "Detected MongoDB authentication failure that is usually fixed by adding"
854
+ warn " ?authSource=admin to the URI."
855
+ if confirm "Append '?authSource=admin' to your MongoDB URI now?"; then
856
+ # Fix the primary SGBD_URI if it's mongodb
857
+ if [[ "${DB_DIALECT:-}" == "mongodb" ]] && [[ -n "${SGBD_URI:-}" ]] && [[ ! "$SGBD_URI" =~ authSource= ]]; then
858
+ local new_uri
859
+ if [[ "$SGBD_URI" =~ \? ]]; then
860
+ new_uri="${SGBD_URI}&authSource=admin"
861
+ else
862
+ new_uri="${SGBD_URI}?authSource=admin"
863
+ fi
864
+ save_var SGBD_URI "$new_uri"
865
+ ok "Updated SGBD_URI : $new_uri"
866
+ fi
867
+ # Fix any MongoDB extra binding missing authSource
868
+ if [[ -n "${EXTRA_BINDINGS:-}" ]]; then
869
+ local new_bindings=""
870
+ local IFS=';'
871
+ for b in $EXTRA_BINDINGS; do
872
+ local name="${b%%:*}"
873
+ local rest="${b#*:}"
874
+ local dialect="${rest%%:*}"
875
+ local uri="${rest#*:}"
876
+ if [[ "$dialect" == "mongodb" ]] && [[ ! "$uri" =~ authSource= ]]; then
877
+ if [[ "$uri" =~ \? ]]; then
878
+ uri="${uri}&authSource=admin"
879
+ else
880
+ uri="${uri}?authSource=admin"
881
+ fi
882
+ fi
883
+ new_bindings+="${new_bindings:+;}${name}:${dialect}:${uri}"
884
+ done
885
+ IFS=$' \t\n'
886
+ save_var EXTRA_BINDINGS "$new_bindings"
887
+ ok "Updated EXTRA_BINDINGS"
888
+ fi
889
+ echo
890
+ info "Re-running the test with the fixed URI..."
891
+ # Rebuild args with fresh env
892
+ load_env
893
+ args=()
894
+ [[ -n "${DB_DIALECT:-}" && -n "${SGBD_URI:-}" ]] && args+=("${DB_DIALECT}|${SGBD_URI}")
895
+ if [[ -n "${EXTRA_BINDINGS:-}" ]]; then
896
+ local IFS=';'
897
+ for b in $EXTRA_BINDINGS; do
898
+ local rest="${b#*:}"
899
+ args+=("${rest%%:*}|${rest#*:}")
900
+ done
901
+ IFS=$' \t\n'
902
+ fi
903
+ node "$CONFIG_DIR/test-connections.mjs" "${args[@]}" 2>&1 | tee "$LOG_DIR/test-connections.log"
904
+ fi
905
+ fi
906
+
804
907
  pause
805
908
  }
806
909
 
@@ -908,6 +1011,21 @@ const schemaStrategy = process.env.DB_SCHEMA_STRATEGY ?? 'update';
908
1011
  const poolSize = parseInt(process.env.DB_POOL_SIZE ?? '20', 10);
909
1012
  const showSql = process.env.DB_SHOW_SQL === 'true';
910
1013
 
1014
+ function hintFor(dialect, rawUri, err) {
1015
+ const msg = (err && err.message) ? err.message : '';
1016
+ const code = err && (err.code ?? err.codeName);
1017
+ if (dialect === 'mongodb' && (code === 18 || /AuthenticationFailed|18/.test(msg))) {
1018
+ if (!/authSource=/.test(rawUri)) {
1019
+ return 'Missing ?authSource=admin on MongoDB URI. Try :\n '
1020
+ + (rawUri.includes('?') ? rawUri + '&authSource=admin' : rawUri + '?authSource=admin');
1021
+ }
1022
+ }
1023
+ if (/ECONNREFUSED/i.test(msg)) return 'Service not running at that host/port';
1024
+ if (/ENOTFOUND/i.test(msg)) return 'DNS resolution failed';
1025
+ if (/ETIMEDOUT/i.test(msg)) return 'Connection timed out';
1026
+ return null;
1027
+ }
1028
+
911
1029
  for (const [dialect, rawUri] of pairs) {
912
1030
  const uri = dialect === 'sqlite' ? stripScheme(rawUri) : rawUri;
913
1031
  process.stdout.write('→ ' + dialect.padEnd(12) + ' : ' + rawUri + '\n');
@@ -920,6 +1038,8 @@ for (const [dialect, rawUri] of pairs) {
920
1038
  } catch (e) {
921
1039
  console.error(' ✗ ' + dialect + ' failed : ' + (e.message ?? e));
922
1040
  if (e.code) console.error(' code : ' + e.code);
1041
+ const hint = hintFor(dialect, rawUri, e);
1042
+ if (hint) console.error(' → ' + hint);
923
1043
  fail++;
924
1044
  }
925
1045
  }
@@ -1144,9 +1264,79 @@ svc_start_dev() {
1144
1264
  }
1145
1265
 
1146
1266
  svc_start_mostanet() {
1147
- warn "mosta-net server not yet installed. Provision with :"
1148
- dim " $PKG_MANAGER install @mostajs/net"
1149
- dim " then run : npx mosta-net --entities .mostajs/generated/entities.ts"
1267
+ load_env
1268
+ cd "$PROJECT_ROOT" || return
1269
+
1270
+ if [[ ! -f "$GENERATED_DIR/entities.json" ]]; then
1271
+ err "No entities.json — run menu 1 (Convert) first"
1272
+ return
1273
+ fi
1274
+
1275
+ # Ensure @mostajs/net AND its peer deps are installed
1276
+ info "Checking @mostajs/net + peer dependencies..."
1277
+ if ! ensure_pkg "@mostajs/net" "@mostajs/orm" "@mostajs/mproject" "@mostajs/replicator"; then
1278
+ err "Cannot start mosta-net server without these packages."
1279
+ return
1280
+ fi
1281
+
1282
+ # Make our entities available to mostajs-net via schemas.json
1283
+ # (mostajs-net's server.js looks for ./schemas.json in CWD)
1284
+ if [[ ! -L schemas.json && ! -f schemas.json ]]; then
1285
+ ln -sf "$GENERATED_DIR/entities.json" "$PROJECT_ROOT/schemas.json" 2>/dev/null \
1286
+ || cp "$GENERATED_DIR/entities.json" "$PROJECT_ROOT/schemas.json"
1287
+ ok "Linked schemas.json → $GENERATED_DIR/entities.json"
1288
+ fi
1289
+
1290
+ # Derive port from MOSTA_NET_URL
1291
+ local mosta_port=14488
1292
+ if [[ "${MOSTA_NET_URL:-}" =~ :([0-9]+) ]]; then
1293
+ mosta_port="${BASH_REMATCH[1]}"
1294
+ fi
1295
+
1296
+ # Prepare env for the child process
1297
+ # - DB vars : DB_DIALECT, SGBD_URI, DB_SCHEMA_STRATEGY, ...
1298
+ # - MOSTA_NET_PORT : derived from MOSTA_NET_URL
1299
+ # - MOSTA_NET_<transport>_ENABLED=true
1300
+ local transport="${MOSTA_NET_TRANSPORT:-rest}"
1301
+ local tr_upper="$(echo "$transport" | tr '[:lower:]' '[:upper:]')"
1302
+
1303
+ info "Launching mostajs-net serve (port $mosta_port, transport $transport)"
1304
+ info "Logs → $LOG_DIR/mostanet.log"
1305
+
1306
+ local launcher
1307
+ if [[ -x "$PROJECT_ROOT/node_modules/.bin/mostajs-net" ]]; then
1308
+ launcher="$PROJECT_ROOT/node_modules/.bin/mostajs-net"
1309
+ else
1310
+ launcher="npx mostajs-net"
1311
+ fi
1312
+
1313
+ # Launch detached with all env vars
1314
+ (
1315
+ export DB_DIALECT="${DB_DIALECT:-sqlite}"
1316
+ export SGBD_URI="${SGBD_URI:-./data.sqlite}"
1317
+ export DB_SCHEMA_STRATEGY="${DB_SCHEMA_STRATEGY:-update}"
1318
+ export DB_POOL_SIZE="${DB_POOL_SIZE:-20}"
1319
+ export DB_SHOW_SQL="${DB_SHOW_SQL:-false}"
1320
+ export MOSTA_NET_PORT="$mosta_port"
1321
+ export "MOSTA_NET_${tr_upper}_ENABLED"="true"
1322
+ # Also enable MCP alongside — commonly wanted for AI integrations
1323
+ [[ "$tr_upper" != "MCP" && "${MOSTA_NET_ALSO_MCP:-true}" == "true" ]] && export MOSTA_NET_MCP_ENABLED=true
1324
+ nohup $launcher serve >> "$LOG_DIR/mostanet.log" 2>&1 &
1325
+ echo "$!" > "$LOG_DIR/mostanet.pid"
1326
+ )
1327
+ local pid
1328
+ pid=$(cat "$LOG_DIR/mostanet.pid" 2>/dev/null || echo "?")
1329
+ ok "Started (PID $pid)"
1330
+ echo
1331
+ info "Endpoints (wait 2-3 seconds then hit them) :"
1332
+ dim " REST CRUD : http://localhost:${mosta_port}/api/v1/<collection>"
1333
+ dim " (collection = snake_case plural, e.g. /api/v1/users)"
1334
+ dim " MCP (AI) : http://localhost:${mosta_port}/mcp"
1335
+ dim " Tail log : tail -f $LOG_DIR/mostanet.log"
1336
+ echo
1337
+ info "Try it :"
1338
+ dim " curl http://localhost:${mosta_port}/api/v1/users"
1339
+ dim " curl http://localhost:${mosta_port}/api/v1/members"
1150
1340
  }
1151
1341
 
1152
1342
  svc_stop_all() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/orm-cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Universal CLI to integrate @mostajs/orm into any project — auto-detects Prisma, OpenAPI, JSON Schema. Interactive menu + subcommands. 13 databases.",
5
5
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
6
  "license": "AGPL-3.0-or-later",