@voltro/database 0.22.1 → 0.24.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/CHANGELOG.md +427 -0
- package/dist/{fileBased-CDnutVVk.js → frameworkLiveTables-BvPIm2cD.js} +174 -174
- package/dist/index.d.ts +40 -0
- package/dist/index.js +651 -651
- package/dist/sql.d.ts +114 -5
- package/dist/sql.js +981 -861
- package/package.json +2 -2
package/dist/sql.d.ts
CHANGED
|
@@ -135,7 +135,13 @@ export declare interface ApplyPlanCtx {
|
|
|
135
135
|
* Caller passes the dialect explicitly. Defaults to `'postgres'` for
|
|
136
136
|
* call-sites that haven't migrated yet.
|
|
137
137
|
*/
|
|
138
|
-
export declare const applySchema: (tables: ReadonlyArray<AnyTable>, dialect?: DialectId
|
|
138
|
+
export declare const applySchema: (tables: ReadonlyArray<AnyTable>, dialect?: DialectId,
|
|
139
|
+
/** NOTIFY channel for the reactive triggers. Pass the SAME value the store
|
|
140
|
+
* was built with. They are two halves of one contract and used to be able to
|
|
141
|
+
* disagree silently: the store listened on its `cdcChannel` while the DDL
|
|
142
|
+
* always emitted on the default, so a custom channel received nothing and
|
|
143
|
+
* raised nothing. */
|
|
144
|
+
cdcChannel?: string) => Effect.Effect<void, never, SqlClient.SqlClient>;
|
|
139
145
|
|
|
140
146
|
/** Per-row backfill body executed by the migration runner. */
|
|
141
147
|
declare type BackfillFunction<TsType = unknown> = (row: Readonly<Record<string, unknown>>) => TsType | Promise<TsType>;
|
|
@@ -184,6 +190,28 @@ export declare type BootMigrationOutcome = {
|
|
|
184
190
|
* producing a complete, non-overlapping partition of the table list. */
|
|
185
191
|
export declare const chunkTables: <T>(xs: ReadonlyArray<T>, n: number) => T[][];
|
|
186
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Classify a single DDL operation for rolling-deploy safety.
|
|
195
|
+
*
|
|
196
|
+
* UNSAFE (breaks old code during the overlap window):
|
|
197
|
+
* - `drop-column` / `drop-table` — old code reads it → error.
|
|
198
|
+
* - `rename-column` / `rename-table` — to old code the old name vanished.
|
|
199
|
+
* - `alter-column-type` — old reads may fail to decode, old writes may be
|
|
200
|
+
* rejected by the new type; even a "widening" is risky to assume.
|
|
201
|
+
* - `alter-column-nullability` → NOT NULL — old code that inserts without the
|
|
202
|
+
* column (or with NULL) is now rejected.
|
|
203
|
+
* - `add-unique` / `add-unique-composite` / `add-check` / `add-foreign-key`
|
|
204
|
+
* — a constraint old writes may violate the instant it exists.
|
|
205
|
+
*
|
|
206
|
+
* SAFE (old code keeps working):
|
|
207
|
+
* - `create-table` / `add-column` (nullable or defaulted) / `add-index` —
|
|
208
|
+
* additive; old code doesn't see it.
|
|
209
|
+
* - `drop-index` / `drop-unique*` / `drop-check` / `drop-foreign-key` —
|
|
210
|
+
* removing enforcement never makes an old query ERROR.
|
|
211
|
+
* - `alter-column-nullability` → nullable, `alter-column-default` — relaxing.
|
|
212
|
+
*/
|
|
213
|
+
export declare const classifyRollingDeploySafety: (op: MigrationOperation) => RollingDeploySafety;
|
|
214
|
+
|
|
187
215
|
declare interface ColumnDefinition<TsType, Type extends ColumnType = ColumnType, HasDefault extends boolean = boolean> {
|
|
188
216
|
readonly type: Type;
|
|
189
217
|
readonly nullable: boolean;
|
|
@@ -706,6 +734,9 @@ declare type ColumnType = 'id' | 'text' | 'integer' | 'real' | 'decimal' | 'bigi
|
|
|
706
734
|
*/
|
|
707
735
|
export declare const declaredSnapshot: (tables: ReadonlyArray<TableLike>, dialect?: DialectId) => SchemaSnapshot;
|
|
708
736
|
|
|
737
|
+
/** The NOTIFY channel a schema emits on when none is configured. */
|
|
738
|
+
export declare const DEFAULT_CDC_CHANNEL = "framework_changes";
|
|
739
|
+
|
|
709
740
|
/**
|
|
710
741
|
* `DEFAULT <expr>` for an array-valued default.
|
|
711
742
|
*
|
|
@@ -757,7 +788,11 @@ export declare const destructiveScope: (raw: string | undefined) => {
|
|
|
757
788
|
export declare const detectReactiveTriggerDrift: (sql: SqlClient.SqlClient, tables: ReadonlyArray<{
|
|
758
789
|
readonly tableName: string;
|
|
759
790
|
readonly isReactive?: boolean;
|
|
760
|
-
}
|
|
791
|
+
}>,
|
|
792
|
+
/** The app's `cdcChannel`. Part of the trigger NAME on any non-default
|
|
793
|
+
* channel, so omitting it here reported every table as missing on a database
|
|
794
|
+
* where every trigger was present. */
|
|
795
|
+
channel?: string) => Effect.Effect<ReactiveTriggerDrift, SqlError_2>;
|
|
761
796
|
|
|
762
797
|
/** Dialect IDs the framework supports. */
|
|
763
798
|
declare type DialectId = 'postgres' | 'mysql' | 'mariadb' | 'mssql' | 'sqlite' | 'turso';
|
|
@@ -815,7 +850,9 @@ export declare const emitDropColumnDdlMysql: (op: Extract<MigrationOperation, {
|
|
|
815
850
|
* Dialect-uniform now, because there is nothing left in it that only one dialect
|
|
816
851
|
* can express.
|
|
817
852
|
*/
|
|
818
|
-
export declare const emitFrameworkBootstrapSql: (tables: ReadonlyArray<AnyTable>, dialect: DialectId
|
|
853
|
+
export declare const emitFrameworkBootstrapSql: (tables: ReadonlyArray<AnyTable>, dialect: DialectId,
|
|
854
|
+
/** NOTIFY channel for the reactive triggers — see `applySchema`. */
|
|
855
|
+
cdcChannel?: string) => string;
|
|
819
856
|
|
|
820
857
|
/**
|
|
821
858
|
* Emit the full schema DDL SCOPED to a tenant namespace. The container is
|
|
@@ -856,7 +893,10 @@ export declare const emitNamespacedSchemaSql: (tables: ReadonlyArray<AnyTable>,
|
|
|
856
893
|
*/
|
|
857
894
|
export declare const emitNamespaceProvisionDdl: (namespace: string, dialect: DialectId, sqliteFile?: string) => string;
|
|
858
895
|
|
|
859
|
-
export declare const emitSchemaSql: (entities: ReadonlyArray<AnyTable | AnyView>, dialect: DialectId
|
|
896
|
+
export declare const emitSchemaSql: (entities: ReadonlyArray<AnyTable | AnyView>, dialect: DialectId,
|
|
897
|
+
/** NOTIFY channel for the reactive triggers. Must match the store's
|
|
898
|
+
* `cdcChannel`, or writes emit where nothing is listening. */
|
|
899
|
+
cdcChannel?: string) => string;
|
|
860
900
|
|
|
861
901
|
declare type EmptyMerge = unknown;
|
|
862
902
|
|
|
@@ -921,6 +961,21 @@ export declare interface FileMigrationContext {
|
|
|
921
961
|
readonly appliedAt: string;
|
|
922
962
|
}
|
|
923
963
|
|
|
964
|
+
/**
|
|
965
|
+
* A migration whose `up()` SUCCEEDED and whose ledger row could not be written.
|
|
966
|
+
*
|
|
967
|
+
* Its own error type because the recovery is the opposite of the ordinary one:
|
|
968
|
+
* do NOT re-run. The change has landed; what is missing is the record that says
|
|
969
|
+
* so, and every future invocation will apply it again until that row exists.
|
|
970
|
+
*/
|
|
971
|
+
export declare class FileMigrationLedgerError extends Error {
|
|
972
|
+
readonly migrationId: string;
|
|
973
|
+
readonly file: string;
|
|
974
|
+
readonly cause: unknown;
|
|
975
|
+
readonly _tag = "FileMigrationLedgerError";
|
|
976
|
+
constructor(migrationId: string, file: string, cause: unknown);
|
|
977
|
+
}
|
|
978
|
+
|
|
924
979
|
export declare interface FileMigrationRunResult {
|
|
925
980
|
readonly applied: ReadonlyArray<{
|
|
926
981
|
id: string;
|
|
@@ -1548,7 +1603,7 @@ export declare interface RawSqlFragment {
|
|
|
1548
1603
|
readonly dependsOn?: ReadonlyArray<string>;
|
|
1549
1604
|
}
|
|
1550
1605
|
|
|
1551
|
-
/** Prefix
|
|
1606
|
+
/** Prefix every reactive trigger name starts with, whatever the channel. */
|
|
1552
1607
|
export declare const REACTIVE_TRIGGER_PREFIX = "framework_changes_";
|
|
1553
1608
|
|
|
1554
1609
|
export declare interface ReactiveTriggerDrift {
|
|
@@ -1559,6 +1614,39 @@ export declare interface ReactiveTriggerDrift {
|
|
|
1559
1614
|
readonly stale: ReadonlyArray<string>;
|
|
1560
1615
|
}
|
|
1561
1616
|
|
|
1617
|
+
/**
|
|
1618
|
+
* The statements that bring the DATABASE's change triggers back in line with
|
|
1619
|
+
* what the schema declares.
|
|
1620
|
+
*
|
|
1621
|
+
* This exists because the diff could not do it and said it could. Reactive
|
|
1622
|
+
* triggers are emitted only by the two full-schema emitters — the CREATE-
|
|
1623
|
+
* everything path for a fresh database, and the framework bootstrap — so a
|
|
1624
|
+
* table that arrived through the PLANNER (every table an existing app has
|
|
1625
|
+
* added since it was created) never got one. The planner has no trigger
|
|
1626
|
+
* dimension at all, so `db plan` reports 0 operations and `db apply` reports
|
|
1627
|
+
* "schema is up to date" while the drift detector is simultaneously telling
|
|
1628
|
+
* you 500 tables have no trigger, and pointing at `voltro db apply` as the
|
|
1629
|
+
* remedy. Reported by a consumer with 525 tables and 27 triggers, all 27 on
|
|
1630
|
+
* framework tables.
|
|
1631
|
+
*
|
|
1632
|
+
* A single instance is unaffected — its own writes reach its own subscribers
|
|
1633
|
+
* through the in-process path — so this is invisible until you scale out, which
|
|
1634
|
+
* is the worst possible time to find it.
|
|
1635
|
+
*
|
|
1636
|
+
* Postgres only: it is the one dialect where reactivity is carried by DDL. The
|
|
1637
|
+
* binlog / Change Tracking readers are configured at runtime from the same
|
|
1638
|
+
* in-memory table list, so they cannot drift from it.
|
|
1639
|
+
*/
|
|
1640
|
+
export declare const reactiveTriggerRepairSql: (input: {
|
|
1641
|
+
readonly tables: ReadonlyArray<AnyTable>;
|
|
1642
|
+
readonly dialect: DialectId;
|
|
1643
|
+
/** Table names the detector found reactive-but-untriggered. */
|
|
1644
|
+
readonly missing: ReadonlyArray<string>;
|
|
1645
|
+
/** Table names the detector found triggered-but-declared-nonReactive. */
|
|
1646
|
+
readonly stale: ReadonlyArray<string>;
|
|
1647
|
+
readonly channel?: string;
|
|
1648
|
+
}) => ReadonlyArray<string>;
|
|
1649
|
+
|
|
1562
1650
|
export declare const releaseMigrationLock: (sql: SqlClient.SqlClient) => Effect.Effect<void, SqlError_2>;
|
|
1563
1651
|
|
|
1564
1652
|
export declare const renderColumnMysql: (col: ColumnSnapshot) => string;
|
|
@@ -1583,6 +1671,27 @@ export declare const rollbackFileBasedMigration: (sql: SqlClient.SqlClient, ctx:
|
|
|
1583
1671
|
durationMs: number;
|
|
1584
1672
|
}, unknown>;
|
|
1585
1673
|
|
|
1674
|
+
/** Verdict for one operation under a rolling deploy. */
|
|
1675
|
+
export declare type RollingDeploySafety = {
|
|
1676
|
+
readonly safe: true;
|
|
1677
|
+
} | {
|
|
1678
|
+
readonly safe: false;
|
|
1679
|
+
/** Why an instance on the OLD code breaks against the new schema. */
|
|
1680
|
+
readonly reason: string;
|
|
1681
|
+
/** The expand/contract move that keeps both code versions working. */
|
|
1682
|
+
readonly remedy: string;
|
|
1683
|
+
};
|
|
1684
|
+
|
|
1685
|
+
/**
|
|
1686
|
+
* The rolling-deploy-unsafe operations in a plan, each paired with its verdict.
|
|
1687
|
+
* Empty ⇒ the whole plan is safe to apply while an old deploy is still live.
|
|
1688
|
+
*/
|
|
1689
|
+
export declare const rollingDeployUnsafeOps: (operations: ReadonlyArray<PlannedOperation>) => ReadonlyArray<{
|
|
1690
|
+
readonly op: PlannedOperation;
|
|
1691
|
+
readonly reason: string;
|
|
1692
|
+
readonly remedy: string;
|
|
1693
|
+
}>;
|
|
1694
|
+
|
|
1586
1695
|
export declare const runFileBasedMigrations: (sql: SqlClient.SqlClient, ctx: RunFileBasedMigrationsCtx) => Effect.Effect<FileMigrationRunResult, unknown>;
|
|
1587
1696
|
|
|
1588
1697
|
export declare interface RunFileBasedMigrationsCtx {
|