@voltro/database 0.21.0 → 0.22.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/dist/sql.d.ts CHANGED
@@ -789,30 +789,31 @@ export declare const emitDropColumnDdlMysql: (op: Extract<MigrationOperation, {
789
789
  }>) => string;
790
790
 
791
791
  /**
792
- * Evolution-aware DDL emitter for framework-managed tables (the
793
- * `_voltro_*` bookkeeping set + the `voltro_isr_cache` runtime
794
- * cache). Adds one extra step between CREATE TABLE and CREATE INDEX:
792
+ * Bootstrap DDL for framework-managed tables — `CREATE TABLE IF NOT EXISTS`, and
793
+ * deliberately almost nothing else.
795
794
  *
796
- * ALTER TABLE <t> ADD COLUMN IF NOT EXISTS <col> <type>
795
+ * It exists for ONE reason: `applyPlan` records into `_voltro_migration_plans`,
796
+ * so that table has to exist before the planner can run. Everything after
797
+ * that — columns, indexes, types, nullability — belongs to the planner, which
798
+ * handles framework tables exactly like user tables, on every dialect.
797
799
  *
798
- * for every column the framework's declared shape carries. Without
799
- * this the CREATE TABLE IF NOT EXISTS path is fine for fresh DBs but
800
- * silently no-ops on existing tables subsequent CREATE INDEX IF
801
- * NOT EXISTS then PARSE-fails when an index references a column the
802
- * framework added in a later release that the live DB never picked
803
- * up (pg error 42703).
800
+ * IT USED TO DO MORE, AND THAT WAS THE BUG. It also emitted
801
+ * `ALTER TABLE ADD COLUMN IF NOT EXISTS` and `CREATE INDEX IF NOT EXISTS`, and
802
+ * the ADD COLUMN half was POSTGRES-ONLY because that is the dialect with the
803
+ * syntax. Framework tables were filtered out of the boot planner at the time, so
804
+ * this was their only evolution path which meant a framework release that added
805
+ * a column reached a postgres user's database on boot and a MariaDB user's never,
806
+ * silently. One `voltro dev`, two behaviours, decided by the driver.
804
807
  *
805
- * Postgres-only the other dialects' framework-bookkeeping path
806
- * doesn't suffer this because @effect/sql-cluster / migrationRunner
807
- * own their own schema. The applier in the per-dialect packages does
808
- * the equivalent work for their own bookkeeping.
808
+ * The filter is asymmetric now (see `plannableLive` in `migrations/boot.ts`) and
809
+ * the planner owns evolution, so the extra steps here are not merely redundant —
810
+ * they are a second, weaker path, and the index step actively hurt: it ran BEFORE
811
+ * the planner could add a column, so an index over a newly-added column failed
812
+ * the boot (pg 42703) rather than waiting one step. Deleted rather than fixed;
813
+ * fixing it would have kept two paths.
809
814
  *
810
- * For user / plugin / cloud tables this path is wrong they need the
811
- * full planner refuse-to-boot semantics on column drops, renames,
812
- * type changes. Framework bookkeeping is a tighter contract:
813
- * additive-only column evolution, no destructive changes, no user
814
- * data ever lives in a `.dropped()` slot. The framework versions
815
- * its own schema additively + can safely ALTER TABLE forward.
815
+ * Dialect-uniform now, because there is nothing left in it that only one dialect
816
+ * can express.
816
817
  */
817
818
  export declare const emitFrameworkBootstrapSql: (tables: ReadonlyArray<AnyTable>, dialect: DialectId) => string;
818
819
 
@@ -1210,6 +1211,15 @@ export declare type MigrationOperation = {
1210
1211
  readonly kind: 'drop-index';
1211
1212
  readonly table: string;
1212
1213
  readonly index: string;
1214
+ } | {
1215
+ readonly kind: 'rename-index';
1216
+ readonly table: string;
1217
+ readonly from: string;
1218
+ readonly to: string;
1219
+ } | {
1220
+ readonly kind: 'rename-table';
1221
+ readonly from: string;
1222
+ readonly to: string;
1213
1223
  } | {
1214
1224
  readonly kind: 'add-unique';
1215
1225
  readonly table: string;
@@ -1720,6 +1730,23 @@ declare interface Table<Name extends string, Fields extends Record<string, Colum
1720
1730
  readonly appliedUniques: ReadonlyArray<TableUnique>;
1721
1731
  readonly appliedFullText: ReadonlyArray<TableFullTextIndex>;
1722
1732
  readonly appliedChecks: ReadonlyArray<TableCheck>;
1733
+ /**
1734
+ * The name this table used to have, set by `.renamedFrom('old_name')`.
1735
+ * Named apart from the METHOD that sets it — one interface cannot carry both.
1736
+ *
1737
+ * The table-level twin of a column's `.renamedFrom()`, and it exists for the
1738
+ * same reason: a rename and a drop+create are structurally identical to a
1739
+ * differ (old table gone, new table present) and mean completely different
1740
+ * things. Without the marker the planner has to assume drop + create, which
1741
+ * for a TABLE is data loss, so it refuses — and the rename can only be done
1742
+ * by hand.
1743
+ *
1744
+ * A chained method, matching a column's `.renamedFrom()` — the two mean the
1745
+ * same thing one level apart, so they read the same. It survives every
1746
+ * subsequent `.index(...)` / `.with(...)` because each rebuild spreads its
1747
+ * options through.
1748
+ */
1749
+ readonly previousTableName?: string;
1723
1750
  /**
1724
1751
  * Composite PRIMARY KEY column set, declared via `.primaryKey(['a','b'])`.
1725
1752
  * When set it REPLACES the single-column `id()` PK assumption: no column
@@ -2056,6 +2083,23 @@ declare interface Table<Name extends string, Fields extends Record<string, Colum
2056
2083
  * says so at boot.
2057
2084
  */
2058
2085
  nonReactive: () => Table<Name, Fields, false, IxNames>;
2086
+ /**
2087
+ * Declare the name this table used to have, so the planner folds what would
2088
+ * otherwise be DROP + CREATE — the loss of every row — into an in-place
2089
+ * `rename-table`.
2090
+ *
2091
+ * Deliberately the SAME SHAPE as a column's `.renamedFrom()`, because the two
2092
+ * mean the same thing one level apart, and a reader who learned one will reach
2093
+ * for the other. It first shipped as a third argument to `table()`; that made
2094
+ * the one API in this family that had to be looked up.
2095
+ *
2096
+ * export const notes = table('archive_notes', { … }).renamedFrom('notes')
2097
+ *
2098
+ * Drop the marker once the rename has been applied in every environment. A
2099
+ * marker whose old table is absent is a silent no-op, so it can sit in source
2100
+ * across a staged rollout.
2101
+ */
2102
+ renamedFrom: (oldName: string) => Table<Name, Fields, Reactive, IxNames>;
2059
2103
  }
2060
2104
 
2061
2105
  /**
@@ -2179,6 +2223,13 @@ export declare interface TableSnapshot {
2179
2223
  * data exists yet), present from introspection.
2180
2224
  */
2181
2225
  readonly rowCount?: number;
2226
+ /**
2227
+ * The name this table previously had (`table(name, fields, { renamedFrom })`).
2228
+ * DECLARED side only — introspection has no way to know. The planner pairs it
2229
+ * against a live table of that name and folds what would otherwise be
2230
+ * DROP + CREATE into a `rename-table`.
2231
+ */
2232
+ readonly renamedFrom?: string;
2182
2233
  }
2183
2234
 
2184
2235
  declare interface TableUnique {