@mostajs/orm-cli 0.4.7 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/mostajs.sh +389 -2
- package/package.json +1 -1
package/bin/mostajs.sh
CHANGED
|
@@ -27,7 +27,7 @@ CLI_NAME="mostajs"
|
|
|
27
27
|
# PATHS — relative to the CALLER's CWD, not the script
|
|
28
28
|
# ============================================================
|
|
29
29
|
|
|
30
|
-
PROJECT_ROOT="$(pwd)"
|
|
30
|
+
PROJECT_ROOT="${PROJECT_ROOT:-$(pwd)}"
|
|
31
31
|
CONFIG_DIR="$PROJECT_ROOT/.mostajs"
|
|
32
32
|
CONFIG_FILE="$CONFIG_DIR/config.env"
|
|
33
33
|
LOG_DIR="$CONFIG_DIR/logs"
|
|
@@ -214,7 +214,16 @@ header() {
|
|
|
214
214
|
# ============================================================
|
|
215
215
|
|
|
216
216
|
load_env() {
|
|
217
|
-
|
|
217
|
+
# Source .mostajs/config.env WITHOUT overriding env vars that are already
|
|
218
|
+
# set (CLI invocation with DB_DIALECT=... mostajs ... takes precedence).
|
|
219
|
+
[[ -f "$CONFIG_FILE" ]] || return
|
|
220
|
+
while IFS='=' read -r key value; do
|
|
221
|
+
[[ -z "$key" || "$key" =~ ^# ]] && continue
|
|
222
|
+
# Trim whitespace from key and strip surrounding quotes from value
|
|
223
|
+
key="${key// /}"
|
|
224
|
+
value="${value%\"}"; value="${value#\"}"
|
|
225
|
+
[[ -z "${!key+x}" ]] && export "$key=$value"
|
|
226
|
+
done < "$CONFIG_FILE"
|
|
218
227
|
}
|
|
219
228
|
|
|
220
229
|
save_var() {
|
|
@@ -2636,6 +2645,364 @@ EOF
|
|
|
2636
2645
|
pause
|
|
2637
2646
|
}
|
|
2638
2647
|
|
|
2648
|
+
# ============================================================
|
|
2649
|
+
# `mostajs init` — scaffold a new project
|
|
2650
|
+
# ============================================================
|
|
2651
|
+
#
|
|
2652
|
+
# Creates every file a fresh project needs to run on the bridge :
|
|
2653
|
+
# - .env with PORT / DB_DIALECT / SGBD_URI / AUTH_SECRET
|
|
2654
|
+
# - prisma/schema.prisma (minimal User model — starting point)
|
|
2655
|
+
# - src/lib/db.ts (createPrismaLikeDb)
|
|
2656
|
+
# - .mostajs/config.env (mirrors .env for the seed-runner)
|
|
2657
|
+
# - .mostajs/generated/entities.json (empty array, filled by menu 1)
|
|
2658
|
+
#
|
|
2659
|
+
# Dialect defaults to sqlite ./data.sqlite. Pass --dialect=postgres etc.
|
|
2660
|
+
# Refuses to overwrite existing files unless --force.
|
|
2661
|
+
|
|
2662
|
+
action_cli_init() {
|
|
2663
|
+
local dialect="sqlite"
|
|
2664
|
+
local uri=""
|
|
2665
|
+
local force=0
|
|
2666
|
+
while [[ $# -gt 0 ]]; do
|
|
2667
|
+
case "$1" in
|
|
2668
|
+
--dialect) dialect="$2"; shift 2 ;;
|
|
2669
|
+
--dialect=*) dialect="${1#*=}"; shift ;;
|
|
2670
|
+
--uri) uri="$2"; shift 2 ;;
|
|
2671
|
+
--uri=*) uri="${1#*=}"; shift ;;
|
|
2672
|
+
--force|-f) force=1; shift ;;
|
|
2673
|
+
*) warn "Unknown flag: $1"; shift ;;
|
|
2674
|
+
esac
|
|
2675
|
+
done
|
|
2676
|
+
|
|
2677
|
+
# Default URIs per dialect
|
|
2678
|
+
if [[ -z "$uri" ]]; then
|
|
2679
|
+
case "$dialect" in
|
|
2680
|
+
sqlite) uri="./data.sqlite" ;;
|
|
2681
|
+
postgres) uri="postgres://user:pass@localhost:5432/mydb" ;;
|
|
2682
|
+
mysql) uri="mysql://user:pass@localhost:3306/mydb" ;;
|
|
2683
|
+
mariadb) uri="mariadb://user:pass@localhost:3306/mydb" ;;
|
|
2684
|
+
mongodb) uri="mongodb://user:pass@localhost:27017/mydb" ;;
|
|
2685
|
+
oracle) uri="oracle://user:pass@localhost:1521/XE" ;;
|
|
2686
|
+
mssql) uri="mssql://user:pass@localhost:1433/mydb" ;;
|
|
2687
|
+
cockroachdb) uri="postgresql://user:pass@localhost:26257/mydb?sslmode=disable" ;;
|
|
2688
|
+
*) uri="./data.sqlite"; dialect="sqlite" ;;
|
|
2689
|
+
esac
|
|
2690
|
+
fi
|
|
2691
|
+
|
|
2692
|
+
header
|
|
2693
|
+
echo -e "${BOLD}${MAGENTA}▶ mostajs init — scaffold a bridge-ready project${RESET}"
|
|
2694
|
+
echo
|
|
2695
|
+
echo -e " Dialect : ${CYAN}${dialect}${RESET}"
|
|
2696
|
+
echo -e " URI : ${DIM}${uri}${RESET}"
|
|
2697
|
+
echo -e " Root : ${DIM}${PROJECT_ROOT}${RESET}"
|
|
2698
|
+
echo
|
|
2699
|
+
|
|
2700
|
+
local created=0 skipped=0
|
|
2701
|
+
|
|
2702
|
+
write_if_missing() {
|
|
2703
|
+
local path="$1"; local content="$2"
|
|
2704
|
+
if [[ -f "$PROJECT_ROOT/$path" && $force -eq 0 ]]; then
|
|
2705
|
+
dim " - skip $path (exists — use --force to overwrite)"
|
|
2706
|
+
((skipped++))
|
|
2707
|
+
return
|
|
2708
|
+
fi
|
|
2709
|
+
mkdir -p "$(dirname "$PROJECT_ROOT/$path")"
|
|
2710
|
+
printf '%s' "$content" > "$PROJECT_ROOT/$path"
|
|
2711
|
+
ok "created $path"
|
|
2712
|
+
((created++))
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
# --- .env ---
|
|
2716
|
+
local secret
|
|
2717
|
+
secret=$(node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" 2>/dev/null || echo 'CHANGE-ME-IN-PROD')
|
|
2718
|
+
write_if_missing ".env" "\
|
|
2719
|
+
# Port — used by next dev / start (reads PORT from here)
|
|
2720
|
+
PORT=3000
|
|
2721
|
+
|
|
2722
|
+
# Database — consumed by @mostajs/orm-bridge (createPrismaLikeDb)
|
|
2723
|
+
DB_DIALECT=${dialect}
|
|
2724
|
+
SGBD_URI=${uri}
|
|
2725
|
+
DB_SCHEMA_STRATEGY=update
|
|
2726
|
+
|
|
2727
|
+
# NextAuth (if you use it)
|
|
2728
|
+
NEXTAUTH_URL=http://localhost:3000
|
|
2729
|
+
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
|
2730
|
+
AUTH_SECRET=${secret}
|
|
2731
|
+
"
|
|
2732
|
+
|
|
2733
|
+
# --- .mostajs/config.env (mirror for the seed-runner) ---
|
|
2734
|
+
write_if_missing ".mostajs/config.env" "\
|
|
2735
|
+
DB_DIALECT=${dialect}
|
|
2736
|
+
SGBD_URI=${uri}
|
|
2737
|
+
DB_SCHEMA_STRATEGY=update
|
|
2738
|
+
APP_PORT=3000
|
|
2739
|
+
"
|
|
2740
|
+
|
|
2741
|
+
# --- .mostajs/generated/entities.json (empty — filled by menu 1) ---
|
|
2742
|
+
write_if_missing ".mostajs/generated/entities.json" "[]
|
|
2743
|
+
"
|
|
2744
|
+
|
|
2745
|
+
# --- prisma/schema.prisma (minimal starter) ---
|
|
2746
|
+
# Prisma's valid providers : sqlite, postgresql, mysql, mongodb, sqlserver, cockroachdb
|
|
2747
|
+
local provider="$dialect"
|
|
2748
|
+
case "$dialect" in
|
|
2749
|
+
postgres|postgresql) provider="postgresql" ;;
|
|
2750
|
+
mssql) provider="sqlserver" ;;
|
|
2751
|
+
mariadb) provider="mysql" ;;
|
|
2752
|
+
oracle|db2|hana|hsqldb|spanner|sybase) provider="sqlite" ;; # Prisma has no native provider — keep sqlite placeholder
|
|
2753
|
+
esac
|
|
2754
|
+
write_if_missing "prisma/schema.prisma" "\
|
|
2755
|
+
// Minimal starter — edit freely. Run 'mostajs' menu 1 to convert to EntitySchema.
|
|
2756
|
+
generator client {
|
|
2757
|
+
provider = \"prisma-client-js\"
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
datasource db {
|
|
2761
|
+
provider = \"${provider}\"
|
|
2762
|
+
url = env(\"DATABASE_URL\")
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
model User {
|
|
2766
|
+
id String @id @default(uuid())
|
|
2767
|
+
email String @unique
|
|
2768
|
+
password String
|
|
2769
|
+
name String?
|
|
2770
|
+
createdAt DateTime @default(now())
|
|
2771
|
+
updatedAt DateTime @updatedAt
|
|
2772
|
+
}
|
|
2773
|
+
"
|
|
2774
|
+
|
|
2775
|
+
# --- src/lib/db.ts (createPrismaLikeDb) ---
|
|
2776
|
+
write_if_missing "src/lib/db.ts" "\
|
|
2777
|
+
// Generated by 'mostajs init' — @mostajs/orm-bridge entry point.
|
|
2778
|
+
// Every Prisma-style db.User.findUnique(...) call below is routed to
|
|
2779
|
+
// @mostajs/orm (13 dialects). Edit DB_DIALECT / SGBD_URI in .env to switch.
|
|
2780
|
+
import { createPrismaLikeDb } from '@mostajs/orm-bridge/prisma-client'
|
|
2781
|
+
|
|
2782
|
+
export const db = createPrismaLikeDb()
|
|
2783
|
+
"
|
|
2784
|
+
|
|
2785
|
+
echo
|
|
2786
|
+
echo -e " ${BOLD}${created}${RESET} file(s) created, ${DIM}${skipped}${RESET} skipped"
|
|
2787
|
+
echo
|
|
2788
|
+
echo -e " ${BOLD}Next steps${RESET} :"
|
|
2789
|
+
echo -e " ${CYAN}1.${RESET} npm install @mostajs/orm @mostajs/orm-bridge @mostajs/orm-cli --legacy-peer-deps"
|
|
2790
|
+
echo -e " ${CYAN}2.${RESET} Edit ${DIM}prisma/schema.prisma${RESET} — add your models"
|
|
2791
|
+
echo -e " ${CYAN}3.${RESET} ${CYAN}mostajs${RESET} → menu 1 (Convert) → menu 3 (init DDL)"
|
|
2792
|
+
echo -e " ${CYAN}4.${RESET} ${CYAN}mostajs${RESET} → menu S (Seeds) — populate, hash, apply"
|
|
2793
|
+
echo -e " ${CYAN}5.${RESET} ${CYAN}npm run dev${RESET}"
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
# ============================================================
|
|
2797
|
+
# `mostajs migrate` — incremental DDL diff / apply / status
|
|
2798
|
+
# ============================================================
|
|
2799
|
+
#
|
|
2800
|
+
# Subcommands :
|
|
2801
|
+
# diff — list ALTERs needed to make the live DB match entities.json
|
|
2802
|
+
# apply — execute those ALTERs (prompts for confirmation, --yes to skip)
|
|
2803
|
+
# status — show entities.json count + live tables count + missing columns
|
|
2804
|
+
|
|
2805
|
+
action_cli_migrate() {
|
|
2806
|
+
local sub="${1:-}"
|
|
2807
|
+
[[ -z "$sub" ]] && { action_migrate_help; return; }
|
|
2808
|
+
shift
|
|
2809
|
+
case "$sub" in
|
|
2810
|
+
diff|d) action_migrate_diff "$@" ;;
|
|
2811
|
+
apply|a) action_migrate_apply "$@" ;;
|
|
2812
|
+
status|s) action_migrate_status "$@" ;;
|
|
2813
|
+
help|h|--help) action_migrate_help ;;
|
|
2814
|
+
*) err "Unknown migrate subcommand: $sub"; action_migrate_help; return 1 ;;
|
|
2815
|
+
esac
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
action_migrate_help() {
|
|
2819
|
+
cat <<EOF
|
|
2820
|
+
|
|
2821
|
+
${BOLD}mostajs migrate${RESET} — incremental schema migration
|
|
2822
|
+
|
|
2823
|
+
${CYAN}diff${RESET} show ALTER statements the DB needs to match entities.json
|
|
2824
|
+
${CYAN}apply${RESET} execute those ALTERs (prompts for confirmation)
|
|
2825
|
+
flags : --yes (skip confirmation)
|
|
2826
|
+
${CYAN}status${RESET} show live-vs-schema summary per entity
|
|
2827
|
+
|
|
2828
|
+
Every subcommand honors DB_DIALECT + SGBD_URI from ${DIM}.mostajs/config.env${RESET}.
|
|
2829
|
+
|
|
2830
|
+
EOF
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2833
|
+
# Node helper : compare live columns vs schema.fields and emit ALTER plan as JSON.
|
|
2834
|
+
# Outputs to stdout : { changes: [{ table, column, sql }], ok: bool }
|
|
2835
|
+
_migrate_compute_plan() {
|
|
2836
|
+
load_env
|
|
2837
|
+
local entities_json="$GENERATED_DIR/entities.json"
|
|
2838
|
+
if [[ ! -f "$entities_json" ]]; then
|
|
2839
|
+
err "No entities.json — run menu 1 (Convert) first."
|
|
2840
|
+
return 1
|
|
2841
|
+
fi
|
|
2842
|
+
ENT_PATH="$entities_json" DIALECT="$DB_DIALECT" URI="$SGBD_URI" \
|
|
2843
|
+
node --input-type=module -e "
|
|
2844
|
+
import { readFileSync } from 'node:fs';
|
|
2845
|
+
import { getDialect } from '${PROJECT_ROOT}/node_modules/@mostajs/orm/dist/index.js';
|
|
2846
|
+
const entities = JSON.parse(readFileSync(process.env.ENT_PATH, 'utf8'));
|
|
2847
|
+
const d = await getDialect({ dialect: process.env.DIALECT, uri: process.env.URI, schemaStrategy: 'none' });
|
|
2848
|
+
|
|
2849
|
+
// Use the dialect's own introspection — protected method, exposed via cast
|
|
2850
|
+
const changes = [];
|
|
2851
|
+
for (const e of entities) {
|
|
2852
|
+
let live;
|
|
2853
|
+
try {
|
|
2854
|
+
live = await (d).getExistingColumns(e.collection);
|
|
2855
|
+
} catch {
|
|
2856
|
+
changes.push({ table: e.collection, column: '*', sql: '-- (cannot introspect — run menu 3 first)' });
|
|
2857
|
+
continue;
|
|
2858
|
+
}
|
|
2859
|
+
const hasCol = (name) => {
|
|
2860
|
+
const lc = name.toLowerCase();
|
|
2861
|
+
for (const c of live) if (c.toLowerCase() === lc) return true;
|
|
2862
|
+
return false;
|
|
2863
|
+
};
|
|
2864
|
+
// Field columns
|
|
2865
|
+
for (const [name, f] of Object.entries(e.fields || {})) {
|
|
2866
|
+
if (name === '_id') continue;
|
|
2867
|
+
if (hasCol(name)) continue;
|
|
2868
|
+
// Reconstruct the ALTER — d has fieldToSqlType + getIdColumnType + quoteIdentifier
|
|
2869
|
+
const q = (n) => (d).quoteIdentifier(n);
|
|
2870
|
+
let sql;
|
|
2871
|
+
if (name === 'id') {
|
|
2872
|
+
sql = 'ALTER TABLE ' + q(e.collection) + ' ADD ' + q('id') + ' ' + (d).getIdColumnType();
|
|
2873
|
+
} else {
|
|
2874
|
+
sql = 'ALTER TABLE ' + q(e.collection) + ' ADD ' + q(name) + ' ' + (d).fieldToSqlType(f);
|
|
2875
|
+
}
|
|
2876
|
+
changes.push({ table: e.collection, column: name, sql });
|
|
2877
|
+
}
|
|
2878
|
+
// Relation FK columns
|
|
2879
|
+
for (const [rname, rel] of Object.entries(e.relations || {})) {
|
|
2880
|
+
if (rel.type !== 'many-to-one' && rel.type !== 'one-to-one') continue;
|
|
2881
|
+
const colName = rel.joinColumn || (rname + 'Id');
|
|
2882
|
+
if (hasCol(colName)) continue;
|
|
2883
|
+
const q = (n) => (d).quoteIdentifier(n);
|
|
2884
|
+
changes.push({
|
|
2885
|
+
table: e.collection, column: colName,
|
|
2886
|
+
sql: 'ALTER TABLE ' + q(e.collection) + ' ADD ' + q(colName) + ' ' + (d).getIdColumnType(),
|
|
2887
|
+
});
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
await d.disconnect();
|
|
2891
|
+
console.log(JSON.stringify({ ok: true, changes }));
|
|
2892
|
+
"
|
|
2893
|
+
}
|
|
2894
|
+
|
|
2895
|
+
action_migrate_diff() {
|
|
2896
|
+
header
|
|
2897
|
+
echo -e "${BOLD}${MAGENTA}▶ mostajs migrate diff${RESET}"
|
|
2898
|
+
echo
|
|
2899
|
+
local plan_json
|
|
2900
|
+
plan_json=$(_migrate_compute_plan) || { pause; return 1; }
|
|
2901
|
+
local count
|
|
2902
|
+
count=$(echo "$plan_json" | node -e "process.stdin.on('data',d=>{console.log(JSON.parse(d).changes.length)})" 2>/dev/null || echo '?')
|
|
2903
|
+
if [[ "$count" == "0" ]]; then
|
|
2904
|
+
ok "Schema is up to date — nothing to ALTER."
|
|
2905
|
+
return 0
|
|
2906
|
+
fi
|
|
2907
|
+
echo -e " ${BOLD}${count}${RESET} pending change(s) :"
|
|
2908
|
+
echo
|
|
2909
|
+
echo "$plan_json" | node -e "
|
|
2910
|
+
let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{
|
|
2911
|
+
const p = JSON.parse(d);
|
|
2912
|
+
for (const ch of p.changes) console.log(' ' + ch.sql + ';');
|
|
2913
|
+
});
|
|
2914
|
+
"
|
|
2915
|
+
echo
|
|
2916
|
+
echo -e " Run ${CYAN}mostajs migrate apply${RESET} to execute these statements."
|
|
2917
|
+
}
|
|
2918
|
+
|
|
2919
|
+
action_migrate_apply() {
|
|
2920
|
+
local auto_yes=0
|
|
2921
|
+
[[ "${1:-}" == "--yes" || "${1:-}" == "-y" ]] && auto_yes=1
|
|
2922
|
+
header
|
|
2923
|
+
echo -e "${BOLD}${MAGENTA}▶ mostajs migrate apply${RESET}"
|
|
2924
|
+
echo
|
|
2925
|
+
local plan_json
|
|
2926
|
+
plan_json=$(_migrate_compute_plan) || return 1
|
|
2927
|
+
local count
|
|
2928
|
+
count=$(echo "$plan_json" | node -e "let d='';process.stdin.on('data',c=>d+=c).on('end',()=>console.log(JSON.parse(d).changes.length))" 2>/dev/null || echo 0)
|
|
2929
|
+
if [[ "$count" == "0" ]]; then
|
|
2930
|
+
ok "Schema is up to date — nothing to ALTER."
|
|
2931
|
+
return 0
|
|
2932
|
+
fi
|
|
2933
|
+
echo " Pending : ${BOLD}${count}${RESET} statement(s)"
|
|
2934
|
+
echo
|
|
2935
|
+
echo "$plan_json" | node -e "let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{for(const ch of JSON.parse(d).changes) console.log(' ' + ch.sql + ';')})"
|
|
2936
|
+
echo
|
|
2937
|
+
if [[ $auto_yes -eq 0 ]]; then
|
|
2938
|
+
if ! confirm "Execute these ALTER statements?"; then
|
|
2939
|
+
dim " Aborted."
|
|
2940
|
+
return
|
|
2941
|
+
fi
|
|
2942
|
+
fi
|
|
2943
|
+
|
|
2944
|
+
# Execute
|
|
2945
|
+
load_env
|
|
2946
|
+
PLAN="$plan_json" DIALECT="$DB_DIALECT" URI="$SGBD_URI" \
|
|
2947
|
+
node --input-type=module -e "
|
|
2948
|
+
import { getDialect } from '${PROJECT_ROOT}/node_modules/@mostajs/orm/dist/index.js';
|
|
2949
|
+
const plan = JSON.parse(process.env.PLAN);
|
|
2950
|
+
const d = await getDialect({ dialect: process.env.DIALECT, uri: process.env.URI, schemaStrategy: 'none' });
|
|
2951
|
+
let ok = 0, fail = 0;
|
|
2952
|
+
for (const ch of plan.changes) {
|
|
2953
|
+
try {
|
|
2954
|
+
await d.executeRun(ch.sql, []);
|
|
2955
|
+
console.log(' ✓ ' + ch.table + '.' + ch.column);
|
|
2956
|
+
ok++;
|
|
2957
|
+
} catch (e) {
|
|
2958
|
+
console.error(' ✗ ' + ch.table + '.' + ch.column + ' : ' + e.message);
|
|
2959
|
+
fail++;
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
await d.disconnect();
|
|
2963
|
+
console.log('\nApplied : ' + ok + ' ok, ' + fail + ' failed');
|
|
2964
|
+
process.exit(fail > 0 ? 1 : 0);
|
|
2965
|
+
"
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
action_migrate_status() {
|
|
2969
|
+
header
|
|
2970
|
+
echo -e "${BOLD}${MAGENTA}▶ mostajs migrate status${RESET}"
|
|
2971
|
+
echo
|
|
2972
|
+
load_env
|
|
2973
|
+
local ent_json="$GENERATED_DIR/entities.json"
|
|
2974
|
+
if [[ ! -f "$ent_json" ]]; then
|
|
2975
|
+
err "No entities.json — run menu 1 (Convert) first."
|
|
2976
|
+
return 1
|
|
2977
|
+
fi
|
|
2978
|
+
ENT_PATH="$ent_json" DIALECT="$DB_DIALECT" URI="$SGBD_URI" \
|
|
2979
|
+
node --input-type=module -e "
|
|
2980
|
+
import { readFileSync } from 'node:fs';
|
|
2981
|
+
import { getDialect } from '${PROJECT_ROOT}/node_modules/@mostajs/orm/dist/index.js';
|
|
2982
|
+
const entities = JSON.parse(readFileSync(process.env.ENT_PATH, 'utf8'));
|
|
2983
|
+
const d = await getDialect({ dialect: process.env.DIALECT, uri: process.env.URI, schemaStrategy: 'none' });
|
|
2984
|
+
let existing = 0, missing = 0, lagging = 0;
|
|
2985
|
+
for (const e of entities) {
|
|
2986
|
+
let live;
|
|
2987
|
+
try { live = await (d).getExistingColumns(e.collection); }
|
|
2988
|
+
catch { live = new Set(); }
|
|
2989
|
+
if (!live || live.size === 0) { console.log(' ✗ ' + e.collection + ' — table not found'); missing++; continue; }
|
|
2990
|
+
const hasCol = (n) => { const lc = n.toLowerCase(); for (const c of live) if (c.toLowerCase() === lc) return true; return false; };
|
|
2991
|
+
const schemaCols = Object.keys(e.fields || {});
|
|
2992
|
+
const need = schemaCols.filter(c => !hasCol(c));
|
|
2993
|
+
if (need.length) {
|
|
2994
|
+
console.log(' ⚠ ' + e.collection + ' — missing ' + need.length + ' column(s) : ' + need.join(', '));
|
|
2995
|
+
lagging++;
|
|
2996
|
+
} else {
|
|
2997
|
+
console.log(' ✓ ' + e.collection + ' (' + live.size + ' cols live, ' + schemaCols.length + ' in schema)');
|
|
2998
|
+
existing++;
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
await d.disconnect();
|
|
3002
|
+
console.log('\n ' + existing + ' up-to-date · ' + lagging + ' need migrate · ' + missing + ' missing');
|
|
3003
|
+
"
|
|
3004
|
+
}
|
|
3005
|
+
|
|
2639
3006
|
# ============================================================
|
|
2640
3007
|
# CLI SUBCOMMANDS (non-interactive)
|
|
2641
3008
|
# ============================================================
|
|
@@ -2721,6 +3088,26 @@ run_subcommand() {
|
|
|
2721
3088
|
health|h)
|
|
2722
3089
|
action_healthcheck
|
|
2723
3090
|
;;
|
|
3091
|
+
init)
|
|
3092
|
+
# mostajs init [--dialect sqlite|postgres|mongodb|...] [--force]
|
|
3093
|
+
# Scaffold a fresh project with bridge-ready layout :
|
|
3094
|
+
# .env (PORT, DB_DIALECT, SGBD_URI, AUTH_SECRET)
|
|
3095
|
+
# prisma/schema.prisma (minimal — User model only)
|
|
3096
|
+
# src/lib/db.ts (createPrismaLikeDb)
|
|
3097
|
+
# .mostajs/config.env (mirrors .env for the runner)
|
|
3098
|
+
# .mostajs/generated/entities.json (empty array)
|
|
3099
|
+
shift
|
|
3100
|
+
action_cli_init "$@"
|
|
3101
|
+
;;
|
|
3102
|
+
migrate|mig|m)
|
|
3103
|
+
# mostajs migrate <subcommand> [options]
|
|
3104
|
+
# Subcommands :
|
|
3105
|
+
# diff — show ALTER statements the target DB needs to match entities.json
|
|
3106
|
+
# apply — execute those ALTERs (with confirmation)
|
|
3107
|
+
# status — show what's in entities.json vs what's live in the DB
|
|
3108
|
+
shift
|
|
3109
|
+
action_cli_migrate "$@"
|
|
3110
|
+
;;
|
|
2724
3111
|
install-bridge|ib)
|
|
2725
3112
|
# mostajs install-bridge [--apply] [--file X] [--project P] [--restore]
|
|
2726
3113
|
# Codemod : scans the project for `new PrismaClient(...)` sites and rewrites
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mostajs/orm-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Universal CLI to integrate @mostajs/orm into any project — one-shot `mostajs bootstrap` migrates a Prisma project (codemod + deps + schema convert + DDL) to 13 databases with zero code change.",
|
|
5
5
|
"author": "Dr Hamid MADANI <drmdh@msn.com>",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|