@mostajs/orm-cli 0.5.17 → 0.6.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.
Files changed (2) hide show
  1. package/bin/mostajs.sh +198 -6
  2. package/package.json +1 -1
package/bin/mostajs.sh CHANGED
@@ -4068,20 +4068,212 @@ CFG
4068
4068
  exit 1
4069
4069
  fi
4070
4070
 
4071
- # ─── Step 4 ───
4072
- echo -e "\n\e[1m▶ Step 4/4 : done\e[0m"
4071
+ # ─── Step 4 : Data migration ───
4072
+ echo -e "\n\e[1m▶ Step 4/6 : Data migration\e[0m"
4073
+ echo
4074
+ echo -e " Tables created on ${CYAN}$DB_DIALECT${RESET} (${DIM}$SGBD_URI${RESET})."
4075
+ echo -e " How do you want to populate them?"
4076
+ echo
4077
+ echo -e " ${CYAN}1${RESET}) Apply seed files ${DIM}(.mostajs/seeds/*.json → mostajs seed)${RESET}"
4078
+ echo -e " ${CYAN}2${RESET}) Replicate from old DB ${DIM}(old Prisma DB = master → new DB = slave)${RESET}"
4079
+ echo -e " ${CYAN}3${RESET}) Start with empty tables ${DIM}(app handles its own data)${RESET}"
4080
+ echo -e " ${CYAN}4${RESET}) Auto-replicate old → new ${DIM}(init schema + snapshot all data)${RESET}"
4081
+ echo
4082
+ local dm_choice
4083
+ dm_choice=$(ask "Choice" "3")
4084
+
4085
+ case "$dm_choice" in
4086
+ 1)
4087
+ info " Run menu S to upload + apply seeds, or place JSON files in .mostajs/seeds/"
4088
+ ;;
4089
+ 3)
4090
+ ok " Empty tables — ready for your app to populate."
4091
+ ;;
4092
+ 2|4)
4093
+ # ─── Replication from old DB ───
4094
+ echo
4095
+ echo -e "${BOLD}▶ Replication setup : old DB → new DB${RESET}"
4096
+
4097
+ # Detect old Prisma DATABASE_URL
4098
+ local old_uri=""
4099
+ if [[ -f "$PROJECT_ROOT/.env" ]]; then
4100
+ old_uri=$(grep -E '^DATABASE_URL=' "$PROJECT_ROOT/.env" 2>/dev/null | head -1 | sed 's/^DATABASE_URL=//' | tr -d '"' | tr -d "'")
4101
+ fi
4102
+ if [[ -z "$old_uri" ]]; then
4103
+ old_uri=$(ask "Old Prisma DATABASE_URL (source)" "postgresql://user:pass@localhost:5432/olddb")
4104
+ else
4105
+ info " Detected old DATABASE_URL from .env"
4106
+ echo -e " ${DIM}$old_uri${RESET}"
4107
+ if ! confirm "Use this as source?"; then
4108
+ old_uri=$(ask "Old DATABASE_URL" "$old_uri")
4109
+ fi
4110
+ fi
4111
+
4112
+ # Detect old dialect from URI
4113
+ local old_dialect="postgres"
4114
+ case "$old_uri" in
4115
+ postgresql://*|postgres://*) old_dialect="postgres" ;;
4116
+ mysql://*) old_dialect="mysql" ;;
4117
+ mongodb://*) old_dialect="mongodb" ;;
4118
+ mariadb://*) old_dialect="mariadb" ;;
4119
+ oracle://*) old_dialect="oracle" ;;
4120
+ mssql://*) old_dialect="mssql" ;;
4121
+ *sqlite*|*.db|*.sqlite) old_dialect="sqlite" ;;
4122
+ esac
4123
+ info " Source dialect : $old_dialect"
4124
+ info " Target dialect : $DB_DIALECT"
4125
+
4126
+ # Install replicator + monitor deps
4127
+ echo
4128
+ echo -e "${CYAN}▶ Installing replicator + monitor deps${RESET}"
4129
+ ensure_pkg "@mostajs/replicator" "@mostajs/replica-monitor" "@mostajs/mproject" || {
4130
+ err "Failed to install replicator deps"
4131
+ }
4132
+
4133
+ # Write replicator-tree.json
4134
+ echo
4135
+ echo -e "${CYAN}▶ Creating replicator tree (old → new)${RESET}"
4136
+ local tree_file="$CONFIG_DIR/replicator-tree.json"
4137
+ cat > "$tree_file" <<TREE
4138
+ {
4139
+ "replicas": {
4140
+ "old-db": {
4141
+ "source": {
4142
+ "role": "master",
4143
+ "dialect": "$old_dialect",
4144
+ "uri": "$old_uri",
4145
+ "schemaStrategy": "none"
4146
+ }
4147
+ },
4148
+ "new-db": {
4149
+ "target": {
4150
+ "role": "master",
4151
+ "dialect": "$DB_DIALECT",
4152
+ "uri": "$SGBD_URI",
4153
+ "schemaStrategy": "update"
4154
+ }
4155
+ }
4156
+ },
4157
+ "rules": {
4158
+ "migrate-all": {
4159
+ "source": "old-db",
4160
+ "target": "new-db",
4161
+ "mode": "snapshot",
4162
+ "collections": ["*"],
4163
+ "conflictResolution": "source-wins",
4164
+ "enabled": true
4165
+ }
4166
+ },
4167
+ "routing": {}
4168
+ }
4169
+ TREE
4170
+ ok " Tree written : $tree_file"
4171
+
4172
+ # Scaffold services
4173
+ echo
4174
+ echo -e "${CYAN}▶ Scaffolding services${RESET}"
4175
+ action_rep_scaffold_services
4176
+
4177
+ # If option 4 : run one-shot sync now
4178
+ if [[ "$dm_choice" == "4" ]]; then
4179
+ echo
4180
+ echo -e "${BOLD}▶ Running one-shot data migration (snapshot sync)${RESET}"
4181
+ TREE_FILE="$tree_file" RUNTIME_ROOT="$PROJECT_ROOT" \
4182
+ node --input-type=module -e "
4183
+ import { readFileSync, existsSync } from 'node:fs';
4184
+ import { resolve } from 'node:path';
4185
+
4186
+ const orm = await import(resolve(process.env.RUNTIME_ROOT, 'node_modules/@mostajs/orm/dist/index.js'));
4187
+ const { ReplicationManager } = await import(resolve(process.env.RUNTIME_ROOT, 'node_modules/@mostajs/replicator/dist/index.js'));
4188
+
4189
+ // Load schemas
4190
+ const entPath = resolve(process.env.RUNTIME_ROOT, '.mostajs/generated/entities.json');
4191
+ let schemas = [];
4192
+ if (existsSync(entPath)) {
4193
+ schemas = JSON.parse(readFileSync(entPath, 'utf8'));
4194
+ orm.registerSchemas(schemas);
4195
+ }
4196
+
4197
+ const tree = JSON.parse(readFileSync(process.env.TREE_FILE, 'utf8'));
4198
+ const rm = new ReplicationManager();
4199
+
4200
+ // Connect source + target
4201
+ for (const [project, reps] of Object.entries(tree.replicas)) {
4202
+ for (const [name, cfg] of Object.entries(reps)) {
4203
+ try {
4204
+ await rm.addReplica(project, {
4205
+ name, role: cfg.role, dialect: cfg.dialect,
4206
+ uri: cfg.uri, schemaStrategy: cfg.schemaStrategy ?? 'update',
4207
+ }, schemas);
4208
+ console.log(' ✓ connected ' + project + '/' + name + ' [' + cfg.dialect + ']');
4209
+ } catch (e) {
4210
+ console.error(' ✗ ' + project + '/' + name + ' : ' + e.message);
4211
+ }
4212
+ }
4213
+ }
4214
+
4215
+ // Resolve wildcard + sync
4216
+ for (const [ruleName, rule] of Object.entries(tree.rules)) {
4217
+ try {
4218
+ // Resolve entity names from schemas
4219
+ let collections = rule.collections;
4220
+ if (collections.includes('*')) {
4221
+ collections = schemas.map(s => s.name);
4222
+ console.log(' → expanded * → ' + collections.length + ' entities');
4223
+ }
4224
+ rm.addReplicationRule({ name: ruleName, ...rule, collections });
4225
+ const stats = await rm.sync(ruleName);
4226
+ const ins = stats.recordsSynced ?? 0;
4227
+ const fail = stats.errors ?? 0;
4228
+ console.log(' ✓ ' + ruleName + ' : ' + ins + ' records synced, ' + fail + ' errors (' + (stats.duration ?? 0) + 'ms)');
4229
+ if (stats.details) {
4230
+ for (const d of stats.details) {
4231
+ console.log(' ' + d.collection + ' : ' + d.created + '↑ ' + d.updated + '⟳ ' + d.deleted + '↓ ' + d.errors + '✗');
4232
+ }
4233
+ }
4234
+ } catch (e) {
4235
+ console.error(' ✗ ' + ruleName + ' : ' + e.message);
4236
+ }
4237
+ }
4238
+
4239
+ await rm.disconnectAll();
4240
+ " 2>&1 | sed 's/^/ /'
4241
+ else
4242
+ info " Replication configured. Run 'npm run dev:all' to start continuous sync."
4243
+ info " Or run a one-shot sync : npm run sync-once"
4244
+ fi
4245
+ ;;
4246
+ *)
4247
+ warn " Invalid choice — defaulting to empty tables."
4248
+ ;;
4249
+ esac
4250
+
4251
+ # ─── Step 5 : Summary ───
4252
+ echo -e "\n\e[1m▶ Step 5/6 : Summary\e[0m"
4073
4253
  if (( BS_OK_CODEMOD && BS_OK_DEPS && BS_OK_CONVERT && BS_OK_DDL )); then
4074
4254
  cat <<DONE
4075
4255
 
4076
4256
  ✓ Bridge installed in-place. Original files backed up as *.prisma.bak
4077
4257
  ✓ Schema converted : $GENERATED_DIR/entities.json
4078
4258
  ✓ DDL applied (DB_DIALECT=$DB_DIALECT SGBD_URI=$SGBD_URI)
4259
+ DONE
4260
+ if [[ "$dm_choice" == "2" || "$dm_choice" == "4" ]]; then
4261
+ cat <<DONE
4262
+ ✓ Replicator configured : old ($old_dialect) → new ($DB_DIALECT)
4263
+ ✓ Services scaffolded : services/replicator.mjs + services/monitor.mjs
4264
+ ✓ package.json patched : npm run replicator / monitor / dev:all
4265
+ DONE
4266
+ fi
4267
+ if [[ "$dm_choice" == "4" ]]; then
4268
+ echo " ✓ Data migrated via snapshot sync"
4269
+ fi
4270
+ cat <<DONE
4079
4271
 
4080
4272
  Next :
4081
- - Add seeds to $CONFIG_DIR/seeds/*.json (one file per entity)
4082
- - $CLI_NAME # menu S h (hash) 4 (apply)
4083
- - npm run dev
4084
- - Open http://localhost:3000/login
4273
+ - npm run dev # start your app
4274
+ - npm run dev:all # start app + replicator + monitor
4275
+ - $CLI_NAME # interactive CLI for seeds, replicas, CDC
4276
+ - Open http://localhost:3000
4085
4277
 
4086
4278
  To undo the codemod :
4087
4279
  $CLI_NAME install-bridge --restore --apply
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/orm-cli",
3
- "version": "0.5.17",
3
+ "version": "0.6.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",