@voltro/database 0.24.0 → 0.26.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
@@ -143,6 +143,25 @@ export declare const applySchema: (tables: ReadonlyArray<AnyTable>, dialect?: Di
143
143
  * raised nothing. */
144
144
  cdcChannel?: string) => Effect.Effect<void, never, SqlClient.SqlClient>;
145
145
 
146
+ /**
147
+ * The OPT-IN refuse gate over the advisory. By default (`VOLTRO_ROLLING_DEPLOY`
148
+ * unset) this never fires — the classifier stays a `db plan` advisory, because
149
+ * the framework can't know the deploy strategy and a maintenance-window /
150
+ * scale-to-zero deploy has no overlap window. An operator who DOES rolling
151
+ * deploys opts in with `VOLTRO_ROLLING_DEPLOY=1`, turning an unsafe `db apply`
152
+ * into a hard refusal they must split (expand/contract) or explicitly `--force`.
153
+ *
154
+ * Pure — the caller supplies the resolved flags + the unsafe-op count (the ⚠
155
+ * detail is already printed by the plan renderer), so the decision is testable
156
+ * without a CLI or a DB, and the cloud managed-hosting migration wall can reuse
157
+ * exactly this verdict.
158
+ */
159
+ export declare const assessRollingDeployGate: (input: {
160
+ readonly enabled: boolean;
161
+ readonly forced: boolean;
162
+ readonly unsafeCount: number;
163
+ }) => RollingDeployGateVerdict;
164
+
146
165
  /** Per-row backfill body executed by the migration runner. */
147
166
  declare type BackfillFunction<TsType = unknown> = (row: Readonly<Record<string, unknown>>) => TsType | Promise<TsType>;
148
167
 
@@ -380,6 +399,23 @@ declare interface ColumnDefinition<TsType, Type extends ColumnType = ColumnType,
380
399
  * COSTS is per command — see `serverOnly()` below for the matrix.
381
400
  */
382
401
  readonly serverOnly?: boolean;
402
+ /**
403
+ * Optimistic-concurrency marker, set by `.version()`.
404
+ *
405
+ * The column the store INCREMENTS on every update, and the one an update may
406
+ * carry an expectation of. Two clients that read the same row and both write
407
+ * it are the normal case, not an exotic one — and without this the second
408
+ * write silently wins, which is the shape of every "my change disappeared"
409
+ * report ever filed.
410
+ *
411
+ * A TIMESTAMP cannot do this job. Two writes in the same millisecond are
412
+ * indistinguishable, and across replicas the clocks disagree; this repo has
413
+ * already lost rows to exactly that (an analytics sink dropped 7 of 40 events
414
+ * written in the same millisecond as the query bounding them). A monotonic
415
+ * integer has neither problem, and it also answers the other half of the
416
+ * question — which of two copies is NEWER — without consulting a clock.
417
+ */
418
+ readonly versionColumn?: boolean;
383
419
  /**
384
420
  * Data-sensitivity classification, set by `.sensitive(class)`. Declares that
385
421
  * this column holds personal / sensitive data of a given CLASS (`'email'`,
@@ -1671,6 +1707,15 @@ export declare const rollbackFileBasedMigration: (sql: SqlClient.SqlClient, ctx:
1671
1707
  durationMs: number;
1672
1708
  }, unknown>;
1673
1709
 
1710
+ export declare interface RollingDeployGateVerdict {
1711
+ /** The apply MUST NOT proceed (opt-in gate tripped, not forced). */
1712
+ readonly refuse: boolean;
1713
+ /** The gate tripped but `--force` waved it through — surface a warning. */
1714
+ readonly warnForced: boolean;
1715
+ /** Empty unless refuse or warnForced. */
1716
+ readonly message: string;
1717
+ }
1718
+
1674
1719
  /** Verdict for one operation under a rolling deploy. */
1675
1720
  export declare type RollingDeploySafety = {
1676
1721
  readonly safe: true;
package/dist/sql.js CHANGED
@@ -1,4 +1,4 @@
1
- import { H as e, U as t, a as n, c as r, d as i, ft as a, i as o, n as s, o as c, ot as l, q as u, r as d, s as f } from "./frameworkLiveTables-BvPIm2cD.js";
1
+ import { H as e, U as t, a as n, c as r, d as i, ft as a, i as o, n as s, o as c, ot as l, q as u, r as d, s as f } from "./frameworkLiveTables-BQwB8K6R.js";
2
2
  import { Effect as p } from "effect";
3
3
  import { createLogger as m } from "@voltro/logger";
4
4
  import { SqlClient as h } from "@effect/sql";
@@ -3008,10 +3008,26 @@ BEGIN
3008
3008
  });
3009
3009
  }
3010
3010
  return t;
3011
- }, ni = (e, ...t) => ({
3011
+ }, ni = (e) => {
3012
+ if (!e.enabled || e.unsafeCount === 0) return {
3013
+ refuse: !1,
3014
+ warnForced: !1,
3015
+ message: ""
3016
+ };
3017
+ let t = `${e.unsafeCount} operation(s) are UNSAFE under a rolling deploy (VOLTRO_ROLLING_DEPLOY=1)`;
3018
+ return e.forced ? {
3019
+ refuse: !1,
3020
+ warnForced: !0,
3021
+ message: `${t} — proceeding anyway (--force). Old instances may error mid-rollout.`
3022
+ } : {
3023
+ refuse: !0,
3024
+ warnForced: !1,
3025
+ message: `${t}. Old instances would break against the new schema mid-rollout. Split into expand → deploy → contract (see the ⚠ list above), deploy with no overlap (maintenance window / scale-to-zero), or pass --force to override.`
3026
+ };
3027
+ }, ri = (e, ...t) => ({
3012
3028
  _tag: "RawSqlFragment",
3013
3029
  strings: [...e],
3014
3030
  values: [...t]
3015
- }), ri = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
3031
+ }), ii = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
3016
3032
  //#endregion
3017
- export { O as DEFAULT_CDC_CHANNEL, o as FILE_MIGRATION_PATTERN, Gr as FileMigrationLedgerError, k as REACTIVE_TRIGGER_PREFIX, re as VOLTRO_MIGRATION_LOCK_KEY, T as acquireMigrationLock, et as applyNamespacedSchema, Tr as applyPlan, Je as applySchema, $t as chunkTables, ei as classifyRollingDeploySafety, I as declaredSnapshot, xe as defaultArrayClause, be as defaultClause, M as defaultJsonClause, Br as describeOutcome, Pr as destructiveScope, ft as detectReactiveTriggerDrift, gn as emitDropColumnDdl, _n as emitDropColumnDdlMysql, qe as emitFrameworkBootstrapSql, Ge as emitNamespaceProvisionDdl, Ke as emitNamespacedSchemaSql, We as emitSchemaSql, P as fingerprintSchema, pt as formatReactiveTriggerDrift, Nr as ignoreTablesFromEnv, U as introspectSchema, n as isFileMigration, ri as isRawSqlFragment, Jt as mapPgType, c as migration, N as numericIdSql, qt as parseEnumCheck, Jr as pendingFileMigrationIds, Kt as planMigrations, tt as provisionTenantNamespace, Fe as reactiveTriggerRepairSql, E as releaseMigrationLock, Mn as renderColumnMysql, Dn as renderColumnPg, Zr as rollbackFileBasedMigration, ti as rollingDeployUnsafeOps, Yr as runFileBasedMigrations, nt as runFrameworkBootstrap, $e as runMigrate, zr as runPlannedMigrations, F as shortFingerprint, ct as snapshotColumn, ni as sql, j as sqlType, Qr as squashMigrationPlans, Ir as unblockLossy, D as withMigrationLock };
3033
+ export { O as DEFAULT_CDC_CHANNEL, o as FILE_MIGRATION_PATTERN, Gr as FileMigrationLedgerError, k as REACTIVE_TRIGGER_PREFIX, re as VOLTRO_MIGRATION_LOCK_KEY, T as acquireMigrationLock, et as applyNamespacedSchema, Tr as applyPlan, Je as applySchema, ni as assessRollingDeployGate, $t as chunkTables, ei as classifyRollingDeploySafety, I as declaredSnapshot, xe as defaultArrayClause, be as defaultClause, M as defaultJsonClause, Br as describeOutcome, Pr as destructiveScope, ft as detectReactiveTriggerDrift, gn as emitDropColumnDdl, _n as emitDropColumnDdlMysql, qe as emitFrameworkBootstrapSql, Ge as emitNamespaceProvisionDdl, Ke as emitNamespacedSchemaSql, We as emitSchemaSql, P as fingerprintSchema, pt as formatReactiveTriggerDrift, Nr as ignoreTablesFromEnv, U as introspectSchema, n as isFileMigration, ii as isRawSqlFragment, Jt as mapPgType, c as migration, N as numericIdSql, qt as parseEnumCheck, Jr as pendingFileMigrationIds, Kt as planMigrations, tt as provisionTenantNamespace, Fe as reactiveTriggerRepairSql, E as releaseMigrationLock, Mn as renderColumnMysql, Dn as renderColumnPg, Zr as rollbackFileBasedMigration, ti as rollingDeployUnsafeOps, Yr as runFileBasedMigrations, nt as runFrameworkBootstrap, $e as runMigrate, zr as runPlannedMigrations, F as shortFingerprint, ct as snapshotColumn, ri as sql, j as sqlType, Qr as squashMigrationPlans, Ir as unblockLossy, D as withMigrationLock };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/database",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "Browser-safe schema DSL, query builder, and cross-dialect migration planner for Voltro — one schema, every SQL backend.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@effect/sql": "^0.52.0",
46
- "@voltro/logger": "0.24.0",
46
+ "@voltro/logger": "0.26.0",
47
47
  "typeid-js": "^1.2.0",
48
48
  "ulidx": "^2.4.1"
49
49
  },