@happyvertical/smrt-cli 0.42.7 → 0.43.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/AGENTS.md +19 -0
- package/dist/{commands-BOw91sjd.js → commands-C1bK8bv9.js} +140 -47
- package/dist/index.js +9 -9
- package/package.json +7 -7
package/AGENTS.md
CHANGED
|
@@ -43,6 +43,18 @@ migrations are manifest-driven through registered objects and project manifests.
|
|
|
43
43
|
|
|
44
44
|
## `db:migrate` on PostgreSQL
|
|
45
45
|
|
|
46
|
+
New tables are created in deterministic foreign-key dependency order. Mutual
|
|
47
|
+
cycles are created without the cyclic clauses first, then receive named
|
|
48
|
+
constraints after both tables exist. When a same-package constraint is missing
|
|
49
|
+
on an existing table, `db:migrate` probes the exact child/parent columns for
|
|
50
|
+
orphans before `ADD ... NOT VALID` and `VALIDATE CONSTRAINT`; orphaned data or a
|
|
51
|
+
failed probe stays manual with detector/repair SQL.
|
|
52
|
+
|
|
53
|
+
`db:migrate --dry-run` and deprecated `db:setup --dry-run` print the same
|
|
54
|
+
engine-specific dependency plan used for execution, including exact table DDL
|
|
55
|
+
and deferred PostgreSQL cycle constraints; cached per-class DDL is not a valid
|
|
56
|
+
preview.
|
|
57
|
+
|
|
46
58
|
`db:migrate` always bounds a PostgreSQL batch with `SET LOCAL lock_timeout` and
|
|
47
59
|
`SET LOCAL statement_timeout` inside its transaction, from
|
|
48
60
|
`migrations.postgres.lockTimeout` / `.statementTimeout` (defaults `30s` / `60s`;
|
|
@@ -105,6 +117,13 @@ not from the schema definition.
|
|
|
105
117
|
|
|
106
118
|
## `db:migrate` on SQLite: type changes rebuild the table
|
|
107
119
|
|
|
120
|
+
SQLite can enforce generated same-package constraints on new tables, including
|
|
121
|
+
cycles, but adding one to an existing table requires an explicit rebuild.
|
|
122
|
+
DuckDB reports unsupported constraint shapes or `ALTER ADD CONSTRAINT` as
|
|
123
|
+
manual/refused work, never as a successful no-op. Generated same-package
|
|
124
|
+
constraints retain `ON UPDATE CASCADE`, which DuckDB/JSON cannot enforce, so
|
|
125
|
+
those engines report an actionable refusal rather than silently stripping it.
|
|
126
|
+
|
|
108
127
|
SQLite has no `ALTER COLUMN ... TYPE`, so a type-bucket change (the common one
|
|
109
128
|
being a numeric default edited `0` → `0.0`) is applied as the documented table
|
|
110
129
|
rebuild — stage, copy, drop, rename, replay indexes and triggers — planned by
|
|
@@ -5,7 +5,7 @@ import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, writeFile
|
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
|
-
import { ObjectRegistry, SchemaComparer, checkLiveSchemaParity, createQualifiedName, ensureDeferredSystemTableCompatibility, generateDDLForEngine, getClassName, isQualifiedName, migratePostgresSystemTimestamps, parseQualifiedName, planPostgresSystemTimestampMigrations } from "@happyvertical/smrt-core";
|
|
8
|
+
import { ObjectRegistry, SchemaComparer, checkLiveSchemaParity, createQualifiedName, ensureDeferredSystemTableCompatibility, generateDDLForEngine, getClassName, isQualifiedName, migratePostgresSystemTimestamps, parseQualifiedName, planForeignKeyCreation, planPostgresSystemTimestampMigrations } from "@happyvertical/smrt-core";
|
|
9
9
|
import { loadExternalManifestSync } from "@happyvertical/smrt-core/manifest";
|
|
10
10
|
import { access, cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
11
11
|
import { createLogger } from "@happyvertical/logger";
|
|
@@ -489,6 +489,10 @@ function getSyntheticMigrationNameForAction(action) {
|
|
|
489
489
|
const fingerprint = sqlShapeFingerprint(action);
|
|
490
490
|
return fingerprint ? `add_index_${action.index.name}_${fingerprint}` : `add_index_${action.index.name}`;
|
|
491
491
|
}
|
|
492
|
+
case "add_foreign_key": {
|
|
493
|
+
const fingerprint = sqlShapeFingerprint(action);
|
|
494
|
+
return fingerprint ? `add_foreign_key_${action.tableName}_${fingerprint}` : null;
|
|
495
|
+
}
|
|
492
496
|
case "drop_index": {
|
|
493
497
|
if (!action.indexName) return null;
|
|
494
498
|
const fingerprint = sqlShapeFingerprint(action);
|
|
@@ -517,6 +521,10 @@ function getSyntheticMigrationNameForChange(change) {
|
|
|
517
521
|
});
|
|
518
522
|
return fingerprint ? `add_index_${indexName}_${fingerprint}` : `add_index_${indexName}`;
|
|
519
523
|
}
|
|
524
|
+
case "add_foreign_key": {
|
|
525
|
+
const fingerprint = sqlShapeFingerprint(change);
|
|
526
|
+
return fingerprint ? `add_foreign_key_${change.table}_${fingerprint}` : null;
|
|
527
|
+
}
|
|
520
528
|
case "drop_index": {
|
|
521
529
|
if (!change.name) return null;
|
|
522
530
|
const fingerprint = sqlShapeFingerprint({
|
|
@@ -557,13 +565,13 @@ function shouldApplySchemaMigrations(state) {
|
|
|
557
565
|
return !state.dryRun;
|
|
558
566
|
}
|
|
559
567
|
function classifyFailedMigration(migrationName, unresolvedSyntheticMigrationNames) {
|
|
560
|
-
if (!migrationName.startsWith("add_column_") && !migrationName.startsWith("drop_column_") && !migrationName.startsWith("alter_column_") && !migrationName.startsWith("add_index_") && !migrationName.startsWith("drop_index_") && !migrationName.startsWith("type_upgrade_")) return "other";
|
|
568
|
+
if (!migrationName.startsWith("add_column_") && !migrationName.startsWith("drop_column_") && !migrationName.startsWith("alter_column_") && !migrationName.startsWith("add_foreign_key_") && !migrationName.startsWith("add_index_") && !migrationName.startsWith("drop_index_") && !migrationName.startsWith("type_upgrade_")) return "other";
|
|
561
569
|
return unresolvedSyntheticMigrationNames.has(migrationName) ? "unresolved" : "superseded";
|
|
562
570
|
}
|
|
563
571
|
function getUnresolvedGeneratedMigrationNames(changes) {
|
|
564
572
|
const names = /* @__PURE__ */ new Set();
|
|
565
573
|
for (const change of changes) {
|
|
566
|
-
if (change.type !== "add_column" && change.type !== "drop_column" && change.type !== "alter_column" && change.type !== "add_index" && change.type !== "drop_index" && change.type !== "type_upgrade") continue;
|
|
574
|
+
if (change.type !== "add_column" && change.type !== "drop_column" && change.type !== "alter_column" && change.type !== "add_foreign_key" && change.type !== "add_index" && change.type !== "drop_index" && change.type !== "type_upgrade") continue;
|
|
567
575
|
if (change.type === "type_upgrade" && classifyTypeUpgradeSql(change.sql) === "noop") continue;
|
|
568
576
|
if (change.type === "alter_column" && isAdvisoryOnlyChangeLike(change)) continue;
|
|
569
577
|
for (const migrationName of getSyntheticMigrationNamesForChange(change)) names.add(migrationName);
|
|
@@ -706,6 +714,19 @@ function partitionSchemaChanges(changes, getClassForTable) {
|
|
|
706
714
|
});
|
|
707
715
|
break;
|
|
708
716
|
}
|
|
717
|
+
case "add_foreign_key": {
|
|
718
|
+
const action = {
|
|
719
|
+
type: "add_foreign_key",
|
|
720
|
+
tableName: change.table,
|
|
721
|
+
className,
|
|
722
|
+
sql: change.sql,
|
|
723
|
+
...change.sqlStatements ? { sqlStatements: change.sqlStatements } : {},
|
|
724
|
+
advisory: change.advisory
|
|
725
|
+
};
|
|
726
|
+
if (isAdvisoryOnlyChangeLike(change)) manualInterventions.push(action);
|
|
727
|
+
else migrations.push(action);
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
709
730
|
case "drop_index":
|
|
710
731
|
if (!change.name) continue;
|
|
711
732
|
migrations.push({
|
|
@@ -7752,6 +7773,15 @@ export default testManifest;
|
|
|
7752
7773
|
schemaContract: config.schemaContract
|
|
7753
7774
|
}));
|
|
7754
7775
|
const initOrder = ObjectRegistry.getInitializationOrder();
|
|
7776
|
+
const engine = resolveDDLPreviewEngine(dbType);
|
|
7777
|
+
const schemas = Object.values(ObjectRegistry.getAllSchemasAsDefinitions());
|
|
7778
|
+
const preflightTables = new Set(schemas.map((schema) => schema.tableName));
|
|
7779
|
+
const missingPreflightTables = [...new Set(initOrder.flatMap((className) => {
|
|
7780
|
+
const tableName = ObjectRegistry.getTableName(className);
|
|
7781
|
+
return tableName && !preflightTables.has(tableName) ? [tableName] : [];
|
|
7782
|
+
}))].sort();
|
|
7783
|
+
if (missingPreflightTables.length > 0) throw new Error(`Cannot safely preflight database setup because structured schema definitions are missing for: ${missingPreflightTables.join(", ")}. Rebuild the package manifests before running db:setup.`);
|
|
7784
|
+
const plan = planForeignKeyCreation(schemas, engine);
|
|
7755
7785
|
if (options.verbose) {
|
|
7756
7786
|
console.log("📋 Initialization order (respecting dependencies):");
|
|
7757
7787
|
for (let i = 0; i < initOrder.length; i++) {
|
|
@@ -7764,25 +7794,17 @@ export default testManifest;
|
|
|
7764
7794
|
}
|
|
7765
7795
|
if (options["dry-run"]) {
|
|
7766
7796
|
console.log("📋 SQL Preview (not executed):\n");
|
|
7767
|
-
const
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
const
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
}
|
|
7779
|
-
const schema = await generateSchema(registered.constructor, void 0, { engine: resolveDDLPreviewEngine(dbType) });
|
|
7780
|
-
if (schema && schema.trim() !== "") {
|
|
7781
|
-
const tableName = ObjectRegistry.getTableName(className);
|
|
7782
|
-
console.log(`-- Table: ${tableName} (Class: ${className}, Strategy: ${tableStrategy === "sti" ? "STI" : "CTI"})`);
|
|
7783
|
-
console.log(schema);
|
|
7784
|
-
console.log();
|
|
7785
|
-
}
|
|
7797
|
+
for (const schema of plan.schemas) {
|
|
7798
|
+
const ddl = generateDDLForEngine(schema, engine);
|
|
7799
|
+
console.log(`-- Table: ${schema.tableName}`);
|
|
7800
|
+
console.log(ddl.createTable);
|
|
7801
|
+
for (const statement of [...ddl.indexes, ...ddl.triggers]) console.log(statement);
|
|
7802
|
+
console.log();
|
|
7803
|
+
}
|
|
7804
|
+
if (plan.deferredStatements.length > 0) {
|
|
7805
|
+
console.log("-- Deferred foreign-key constraints");
|
|
7806
|
+
for (const statement of plan.deferredStatements) console.log(statement);
|
|
7807
|
+
console.log();
|
|
7786
7808
|
}
|
|
7787
7809
|
console.log("✅ Dry-run complete (no changes made)\n");
|
|
7788
7810
|
return;
|
|
@@ -7814,23 +7836,42 @@ export default testManifest;
|
|
|
7814
7836
|
}
|
|
7815
7837
|
console.log("\n🗑️ Dropping existing tables...\n");
|
|
7816
7838
|
const dropOrder = [...initOrder].reverse();
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7839
|
+
const dropEngine = resolveDDLPreviewEngine(dbType);
|
|
7840
|
+
if (dropEngine === "sqlite") await db.query("PRAGMA foreign_keys = OFF");
|
|
7841
|
+
try {
|
|
7842
|
+
for (const className of dropOrder) {
|
|
7843
|
+
const tableName = ObjectRegistry.getTableName(className);
|
|
7844
|
+
if (!tableName) continue;
|
|
7845
|
+
try {
|
|
7846
|
+
const cascade = dropEngine === "postgres" ? " CASCADE" : "";
|
|
7847
|
+
await db.query(`DROP TABLE IF EXISTS ${quoteIdentifier(tableName)}${cascade}`);
|
|
7848
|
+
console.log(` ✓ Dropped ${tableName}`);
|
|
7849
|
+
} catch (error) {
|
|
7850
|
+
if (options.verbose) console.log(` ⚠️ Could not drop ${tableName}: ${error}`);
|
|
7851
|
+
throw new Error(`Refusing to continue after failing to drop ${tableName}; dependency or foreign-key constraints may still be active.`, { cause: error });
|
|
7852
|
+
}
|
|
7825
7853
|
}
|
|
7854
|
+
} finally {
|
|
7855
|
+
if (dropEngine === "sqlite") await db.query("PRAGMA foreign_keys = ON");
|
|
7826
7856
|
}
|
|
7827
7857
|
console.log();
|
|
7828
7858
|
}
|
|
7829
7859
|
console.log("🔨 Creating tables...\n");
|
|
7830
|
-
const {
|
|
7860
|
+
const { createSchemaManager } = await import("@happyvertical/smrt-core/schema");
|
|
7861
|
+
const schemaManager = createSchemaManager(db, {
|
|
7862
|
+
engine,
|
|
7863
|
+
skipTriggers: typeof db.exportTable === "function"
|
|
7864
|
+
});
|
|
7865
|
+
try {
|
|
7866
|
+
await schemaManager.ensureTables(schemas);
|
|
7867
|
+
} catch (error) {
|
|
7868
|
+
console.error(` ✗ Schema creation failed: ${error}`);
|
|
7869
|
+
if (options.verbose && error instanceof Error && error.stack) console.error(`\n${error.stack}\n`);
|
|
7870
|
+
throw new Error("Refusing to report database setup success after schema creation failed.", { cause: error });
|
|
7871
|
+
}
|
|
7831
7872
|
let tablesCreated = 0;
|
|
7832
7873
|
let tablesSkipped = 0;
|
|
7833
|
-
for (const className of initOrder)
|
|
7874
|
+
for (const className of initOrder) {
|
|
7834
7875
|
const tableStrategy = ObjectRegistry.getTableStrategy(className);
|
|
7835
7876
|
const stiBase = ObjectRegistry.getSTIBase(className);
|
|
7836
7877
|
const registered = ObjectRegistry.getClass(className);
|
|
@@ -7840,14 +7881,10 @@ export default testManifest;
|
|
|
7840
7881
|
if (options.verbose) console.log(` ⊙ ${className} (shares table with ${stiBase})`);
|
|
7841
7882
|
continue;
|
|
7842
7883
|
}
|
|
7843
|
-
await ensureSchema(db, className);
|
|
7844
7884
|
const tableName = ObjectRegistry.getTableName(className);
|
|
7845
7885
|
const fieldCount = ObjectRegistry.getFields(className)?.size || 0;
|
|
7846
7886
|
console.log(` ✓ ${tableName} (${fieldCount} columns)`);
|
|
7847
7887
|
tablesCreated++;
|
|
7848
|
-
} catch (error) {
|
|
7849
|
-
console.error(` ✗ ${className}: ${error}`);
|
|
7850
|
-
if (options.verbose && error instanceof Error && error.stack) console.error(`\n${error.stack}\n`);
|
|
7851
7888
|
}
|
|
7852
7889
|
console.log();
|
|
7853
7890
|
if (tablesSkipped > 0) {
|
|
@@ -8230,16 +8267,29 @@ export default testManifest;
|
|
|
8230
8267
|
relaxColumns: Boolean(options["relax-columns"]),
|
|
8231
8268
|
postgresTimestampMigration
|
|
8232
8269
|
}).compare(manifestSchemas);
|
|
8270
|
+
const engine = tracker.getEngine();
|
|
8271
|
+
const tablePlan = planForeignKeyCreation(diff.added_tables, engine);
|
|
8272
|
+
const plannedTableDDL = new Map(tablePlan.schemas.map((schema) => [schema.tableName, generateDDLForEngine(schema, engine)]));
|
|
8273
|
+
const deferredForeignKeyMigrations = tablePlan.deferredStatements.map((statement) => {
|
|
8274
|
+
const parsed = statement.match(/^ALTER TABLE\s+"((?:[^"]|"")+)"\s+ADD\s+CONSTRAINT\s+"((?:[^"]|"")+)"/i);
|
|
8275
|
+
const unquote = (value, fallback) => value ? value.replace(/""/g, "\"") : fallback;
|
|
8276
|
+
return {
|
|
8277
|
+
type: "add_foreign_key",
|
|
8278
|
+
tableName: unquote(parsed?.[1], "deferred_cycle"),
|
|
8279
|
+
className: unquote(parsed?.[2], "deferred cycle constraint"),
|
|
8280
|
+
sql: statement
|
|
8281
|
+
};
|
|
8282
|
+
});
|
|
8233
8283
|
const getClassForTable = (tableName) => {
|
|
8234
8284
|
for (const className of initOrder) if (ObjectRegistry.getTableName(className) === tableName) return className;
|
|
8235
8285
|
return tableName;
|
|
8236
8286
|
};
|
|
8237
8287
|
if (diff.added_tables.length > 0 && isDryRun) {
|
|
8238
|
-
for (const schema of
|
|
8288
|
+
for (const schema of tablePlan.schemas) {
|
|
8239
8289
|
const className = getClassForTable(schema.tableName);
|
|
8240
8290
|
const fields = Object.keys(schema.columns).length;
|
|
8241
8291
|
console.log(` 📦 ${schema.tableName} (${className}): Would create table (${fields} columns)`);
|
|
8242
|
-
if (options.verbose
|
|
8292
|
+
if (options.verbose) console.log(` ${plannedTableDDL.get(schema.tableName)?.createTable}`);
|
|
8243
8293
|
}
|
|
8244
8294
|
console.log();
|
|
8245
8295
|
}
|
|
@@ -8247,14 +8297,26 @@ export default testManifest;
|
|
|
8247
8297
|
migrations.push(...partitionedChanges.migrations);
|
|
8248
8298
|
manualInterventions.push(...partitionedChanges.manualInterventions);
|
|
8249
8299
|
const advisories = partitionedChanges.advisories;
|
|
8250
|
-
assertForceMigrationTargetsExist(forceSelection.forceMigrations, [
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8300
|
+
assertForceMigrationTargetsExist(forceSelection.forceMigrations, [
|
|
8301
|
+
...diff.added_tables.map((schema) => `create_table_${schema.tableName}`),
|
|
8302
|
+
...migrations.flatMap((migration) => {
|
|
8303
|
+
const migrationName = getSyntheticMigrationNameForAction(migration);
|
|
8304
|
+
return migrationName ? [migrationName] : [];
|
|
8305
|
+
}),
|
|
8306
|
+
...deferredForeignKeyMigrations.flatMap((migration) => {
|
|
8307
|
+
const migrationName = getSyntheticMigrationNameForAction(migration);
|
|
8308
|
+
return migrationName ? [migrationName] : [];
|
|
8309
|
+
})
|
|
8310
|
+
]);
|
|
8254
8311
|
console.log();
|
|
8255
8312
|
if (manualInterventions.length > 0) {
|
|
8256
8313
|
console.log("⚠️ Schema drift detected that requires manual intervention:\n");
|
|
8257
8314
|
for (const change of manualInterventions) {
|
|
8315
|
+
if (change.type === "add_foreign_key") {
|
|
8316
|
+
console.log(` ${change.tableName}: ${change.advisory?.message ?? change.sql?.replace(/^--\s*/, "") ?? "foreign-key constraint requires manual repair"}`);
|
|
8317
|
+
for (const sql of change.advisory?.suggestedSql ?? []) console.log(` ${sql}`);
|
|
8318
|
+
continue;
|
|
8319
|
+
}
|
|
8258
8320
|
if (!change.mismatch) continue;
|
|
8259
8321
|
const detail = change.type === "type_upgrade" ? `${change.tableName}.${change.mismatch.column}: expected ${change.mismatch.expected}, found ${change.mismatch.actual} (cannot auto-apply on this database engine)` : change.type === "alter_column" ? `${change.tableName}.${change.mismatch.column}: expected ${change.mismatch.expected}, found ${change.mismatch.actual} (${change.alteration ?? "alter_column"}; ${change.sql?.replace(/^--\s*/, "") ?? "cannot auto-apply on this database engine"})` : `${change.tableName}.${change.mismatch.column}: expected ${change.mismatch.expected}, found ${change.mismatch.actual}`;
|
|
8260
8322
|
console.log(` ${detail}`);
|
|
@@ -8284,6 +8346,7 @@ export default testManifest;
|
|
|
8284
8346
|
}
|
|
8285
8347
|
const columnMigrations = migrations.filter((m) => m.type === "add_column");
|
|
8286
8348
|
const indexMigrations = migrations.filter((m) => m.type === "add_index");
|
|
8349
|
+
const foreignKeyMigrations = migrations.filter((m) => m.type === "add_foreign_key");
|
|
8287
8350
|
const indexDrops = migrations.filter((m) => m.type === "drop_index");
|
|
8288
8351
|
const columnAlterations = migrations.filter((m) => m.type === "alter_column");
|
|
8289
8352
|
const columnDrops = migrations.filter((m) => m.type === "drop_column");
|
|
@@ -8312,7 +8375,21 @@ export default testManifest;
|
|
|
8312
8375
|
for (const m of indexMigrations) console.log(` ${m.index?.name} on ${m.tableName}`);
|
|
8313
8376
|
console.log();
|
|
8314
8377
|
}
|
|
8378
|
+
if (foreignKeyMigrations.length > 0) {
|
|
8379
|
+
console.log(` 🔗 Foreign-key constraints to add: ${foreignKeyMigrations.length}`);
|
|
8380
|
+
for (const m of foreignKeyMigrations) console.log(` ${m.tableName}`);
|
|
8381
|
+
console.log();
|
|
8382
|
+
}
|
|
8315
8383
|
console.log(" SQL Statements:\n");
|
|
8384
|
+
for (const schema of tablePlan.schemas) {
|
|
8385
|
+
const ddl = plannedTableDDL.get(schema.tableName);
|
|
8386
|
+
if (!ddl) continue;
|
|
8387
|
+
for (const sql of [
|
|
8388
|
+
ddl.createTable,
|
|
8389
|
+
...ddl.indexes,
|
|
8390
|
+
...ddl.triggers
|
|
8391
|
+
]) console.log(` ${sql}`);
|
|
8392
|
+
}
|
|
8316
8393
|
for (const migration of systemTimestampPreview) {
|
|
8317
8394
|
const terminator = migration.sql.trimEnd().endsWith(";") ? "" : ";";
|
|
8318
8395
|
console.log(` ${migration.sql}${terminator}`);
|
|
@@ -8321,6 +8398,7 @@ export default testManifest;
|
|
|
8321
8398
|
const sqlStatements = m.sqlStatements ?? (m.sql ? [m.sql] : []);
|
|
8322
8399
|
for (const sql of sqlStatements) console.log(` ${sql};`);
|
|
8323
8400
|
}
|
|
8401
|
+
for (const sql of tablePlan.deferredStatements) console.log(` ${sql}`);
|
|
8324
8402
|
console.log();
|
|
8325
8403
|
}
|
|
8326
8404
|
if (!repairData) {
|
|
@@ -8333,13 +8411,13 @@ export default testManifest;
|
|
|
8333
8411
|
let skippedCount = 0;
|
|
8334
8412
|
let errorCount = 0;
|
|
8335
8413
|
let stiErrorCount = 0;
|
|
8336
|
-
const schemaChangeCount = diff.added_tables.length + migrations.length;
|
|
8414
|
+
const schemaChangeCount = diff.added_tables.length + migrations.length + deferredForeignKeyMigrations.length;
|
|
8337
8415
|
if (applySchemaMigrations && schemaChangeCount > 0) {
|
|
8338
8416
|
const migrationDefs = [];
|
|
8339
8417
|
const migrationLogs = /* @__PURE__ */ new Map();
|
|
8340
|
-
const
|
|
8341
|
-
|
|
8342
|
-
|
|
8418
|
+
for (const schema of tablePlan.schemas) {
|
|
8419
|
+
const ddl = plannedTableDDL.get(schema.tableName);
|
|
8420
|
+
if (!ddl) throw new Error(`Cannot create table ${schema.tableName}: planned DDL is unavailable.`);
|
|
8343
8421
|
const createTableSql = ddl.createTable || schema.ddl;
|
|
8344
8422
|
if (!createTableSql?.trim()) throw new Error(`Cannot create table ${schema.tableName}: schema definition has no generated DDL.`);
|
|
8345
8423
|
const migrationName = `create_table_${schema.tableName}`;
|
|
@@ -8357,6 +8435,18 @@ export default testManifest;
|
|
|
8357
8435
|
});
|
|
8358
8436
|
migrationLogs.set(migrationName, { successMessage: `Created table ${schema.tableName} (${fields} columns)` });
|
|
8359
8437
|
}
|
|
8438
|
+
for (const migration of deferredForeignKeyMigrations) {
|
|
8439
|
+
const migrationName = getSyntheticMigrationNameForAction(migration);
|
|
8440
|
+
if (!migrationName) continue;
|
|
8441
|
+
migrationDefs.push({
|
|
8442
|
+
id: migrationName,
|
|
8443
|
+
description: `Add deferred foreign-key constraint ${migration.className} on ${migration.tableName}`,
|
|
8444
|
+
version: "1.0.0",
|
|
8445
|
+
up: migration.sql ? [migration.sql] : [],
|
|
8446
|
+
down: []
|
|
8447
|
+
});
|
|
8448
|
+
migrationLogs.set(migrationName, { successMessage: `Added deferred foreign-key constraint ${migration.className} on ${migration.tableName}` });
|
|
8449
|
+
}
|
|
8360
8450
|
for (const migration of migrations) {
|
|
8361
8451
|
const migrationName = getSyntheticMigrationNameForAction(migration);
|
|
8362
8452
|
if (!migrationName) continue;
|
|
@@ -8380,11 +8470,14 @@ export default testManifest;
|
|
|
8380
8470
|
} else if (migration.type === "drop_index" && migration.indexName) {
|
|
8381
8471
|
migrationSql = migration.sql || "";
|
|
8382
8472
|
actionDesc = `Dropped index ${migration.indexName} on ${migration.tableName}`;
|
|
8473
|
+
} else if (migration.type === "add_foreign_key") {
|
|
8474
|
+
migrationSql = migration.sql || "";
|
|
8475
|
+
actionDesc = `Added foreign-key constraint on ${migration.tableName}`;
|
|
8383
8476
|
} else continue;
|
|
8384
8477
|
const migrationSqlStatements = migration.sqlStatements ?? (migrationSql ? [migrationSql] : []);
|
|
8385
8478
|
migrationDefs.push({
|
|
8386
8479
|
id: migrationName,
|
|
8387
|
-
description: migration.type === "add_column" ? `Add column ${migration.column?.name} to ${migration.tableName}` : migration.type === "alter_column" ? `Alter column ${migration.columnName ?? migration.column?.name} on ${migration.tableName} (${migration.alteration ?? "alter"}: ${migration.mismatch?.actual ?? "?"} → ${migration.mismatch?.expected ?? "?"})` : migration.type === "drop_column" ? `Drop column ${migration.columnName} from ${migration.tableName}` : migration.type === "type_upgrade" ? `Upgrade column ${migration.column?.name} on ${migration.tableName} from ${migration.mismatch?.actual} to ${migration.mismatch?.expected}` : migration.type === "drop_index" ? `Drop index ${migration.indexName} on ${migration.tableName}` : `Add index ${migration.index?.name} on ${migration.tableName}`,
|
|
8480
|
+
description: migration.type === "add_column" ? `Add column ${migration.column?.name} to ${migration.tableName}` : migration.type === "alter_column" ? `Alter column ${migration.columnName ?? migration.column?.name} on ${migration.tableName} (${migration.alteration ?? "alter"}: ${migration.mismatch?.actual ?? "?"} → ${migration.mismatch?.expected ?? "?"})` : migration.type === "drop_column" ? `Drop column ${migration.columnName} from ${migration.tableName}` : migration.type === "type_upgrade" ? `Upgrade column ${migration.column?.name} on ${migration.tableName} from ${migration.mismatch?.actual} to ${migration.mismatch?.expected}` : migration.type === "drop_index" ? `Drop index ${migration.indexName} on ${migration.tableName}` : migration.type === "add_foreign_key" ? `Add foreign-key constraint on ${migration.tableName}` : `Add index ${migration.index?.name} on ${migration.tableName}`,
|
|
8388
8481
|
version: "1.0.0",
|
|
8389
8482
|
up: migrationSqlStatements,
|
|
8390
8483
|
down: []
|
package/dist/index.js
CHANGED
|
@@ -49,63 +49,63 @@ var _playgroundCommands = null;
|
|
|
49
49
|
var _workbenchCommands = null;
|
|
50
50
|
async function getGnodeCommands() {
|
|
51
51
|
if (!_gnodeCommands) {
|
|
52
|
-
const { gnodeCommands } = await import("./commands-
|
|
52
|
+
const { gnodeCommands } = await import("./commands-C1bK8bv9.js");
|
|
53
53
|
_gnodeCommands = gnodeCommands;
|
|
54
54
|
}
|
|
55
55
|
return _gnodeCommands;
|
|
56
56
|
}
|
|
57
57
|
async function getGitCommands() {
|
|
58
58
|
if (!_gitCommands) {
|
|
59
|
-
const { gitCommands } = await import("./commands-
|
|
59
|
+
const { gitCommands } = await import("./commands-C1bK8bv9.js");
|
|
60
60
|
_gitCommands = gitCommands;
|
|
61
61
|
}
|
|
62
62
|
return _gitCommands;
|
|
63
63
|
}
|
|
64
64
|
async function getGenerateCommands() {
|
|
65
65
|
if (!_generateCommands) {
|
|
66
|
-
const { generateCommands } = await import("./commands-
|
|
66
|
+
const { generateCommands } = await import("./commands-C1bK8bv9.js");
|
|
67
67
|
_generateCommands = generateCommands;
|
|
68
68
|
}
|
|
69
69
|
return _generateCommands;
|
|
70
70
|
}
|
|
71
71
|
async function getInitCommands() {
|
|
72
72
|
if (!_initCommands) {
|
|
73
|
-
const { initCommands } = await import("./commands-
|
|
73
|
+
const { initCommands } = await import("./commands-C1bK8bv9.js");
|
|
74
74
|
_initCommands = initCommands;
|
|
75
75
|
}
|
|
76
76
|
return _initCommands;
|
|
77
77
|
}
|
|
78
78
|
async function getUtilityCommands() {
|
|
79
79
|
if (!_utilityCommands) {
|
|
80
|
-
const { utilityCommands } = await import("./commands-
|
|
80
|
+
const { utilityCommands } = await import("./commands-C1bK8bv9.js");
|
|
81
81
|
_utilityCommands = utilityCommands;
|
|
82
82
|
}
|
|
83
83
|
return _utilityCommands;
|
|
84
84
|
}
|
|
85
85
|
async function getDispatchCommands() {
|
|
86
86
|
if (!_dispatchCommands) {
|
|
87
|
-
const { dispatchCommands } = await import("./commands-
|
|
87
|
+
const { dispatchCommands } = await import("./commands-C1bK8bv9.js");
|
|
88
88
|
_dispatchCommands = dispatchCommands;
|
|
89
89
|
}
|
|
90
90
|
return _dispatchCommands;
|
|
91
91
|
}
|
|
92
92
|
async function getDocsCommands() {
|
|
93
93
|
if (!_docsCommands) {
|
|
94
|
-
const { docsCommands } = await import("./commands-
|
|
94
|
+
const { docsCommands } = await import("./commands-C1bK8bv9.js");
|
|
95
95
|
_docsCommands = docsCommands;
|
|
96
96
|
}
|
|
97
97
|
return _docsCommands;
|
|
98
98
|
}
|
|
99
99
|
async function getPlaygroundCommands() {
|
|
100
100
|
if (!_playgroundCommands) {
|
|
101
|
-
const { playgroundCommands } = await import("./commands-
|
|
101
|
+
const { playgroundCommands } = await import("./commands-C1bK8bv9.js");
|
|
102
102
|
_playgroundCommands = playgroundCommands;
|
|
103
103
|
}
|
|
104
104
|
return _playgroundCommands;
|
|
105
105
|
}
|
|
106
106
|
async function getWorkbenchCommands() {
|
|
107
107
|
if (!_workbenchCommands) {
|
|
108
|
-
const { workbenchCommands } = await import("./commands-
|
|
108
|
+
const { workbenchCommands } = await import("./commands-C1bK8bv9.js");
|
|
109
109
|
_workbenchCommands = workbenchCommands;
|
|
110
110
|
}
|
|
111
111
|
return _workbenchCommands;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "Developer CLI for SMRT framework - introspection, testing, and project management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"acorn": "^8.17.0",
|
|
33
33
|
"fast-glob": "3.3.3",
|
|
34
34
|
"tar": "^7.5.19",
|
|
35
|
-
"@happyvertical/smrt-
|
|
36
|
-
"@happyvertical/smrt-
|
|
37
|
-
"@happyvertical/smrt-
|
|
38
|
-
"@happyvertical/smrt-
|
|
39
|
-
"@happyvertical/smrt-
|
|
40
|
-
"@happyvertical/smrt-dev-mcp": "0.
|
|
35
|
+
"@happyvertical/smrt-agents": "0.43.0",
|
|
36
|
+
"@happyvertical/smrt-config": "0.43.0",
|
|
37
|
+
"@happyvertical/smrt-core": "0.43.0",
|
|
38
|
+
"@happyvertical/smrt-playground": "0.43.0",
|
|
39
|
+
"@happyvertical/smrt-types": "0.43.0",
|
|
40
|
+
"@happyvertical/smrt-dev-mcp": "0.43.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "24.13.2",
|