@metaobjectsdev/migrate-ts 0.8.1 → 0.9.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 (115) hide show
  1. package/README.md +1 -3
  2. package/dist/apply/apply.d.ts +61 -0
  3. package/dist/apply/apply.d.ts.map +1 -0
  4. package/dist/apply/apply.js +241 -0
  5. package/dist/apply/apply.js.map +1 -0
  6. package/dist/apply/ledger.d.ts +78 -0
  7. package/dist/apply/ledger.d.ts.map +1 -0
  8. package/dist/apply/ledger.js +146 -0
  9. package/dist/apply/ledger.js.map +1 -0
  10. package/dist/check-expr-compare.d.ts +13 -0
  11. package/dist/check-expr-compare.d.ts.map +1 -0
  12. package/dist/check-expr-compare.js +48 -0
  13. package/dist/check-expr-compare.js.map +1 -0
  14. package/dist/diff/index.d.ts +3 -1
  15. package/dist/diff/index.d.ts.map +1 -1
  16. package/dist/diff/index.js +57 -14
  17. package/dist/diff/index.js.map +1 -1
  18. package/dist/diff/status.js +3 -0
  19. package/dist/diff/status.js.map +1 -1
  20. package/dist/drift/classify.d.ts +16 -0
  21. package/dist/drift/classify.d.ts.map +1 -0
  22. package/dist/drift/classify.js +44 -0
  23. package/dist/drift/classify.js.map +1 -0
  24. package/dist/drift/drift.d.ts +32 -0
  25. package/dist/drift/drift.d.ts.map +1 -0
  26. package/dist/drift/drift.js +36 -0
  27. package/dist/drift/drift.js.map +1 -0
  28. package/dist/emit/d1-safety-pass.d.ts.map +1 -1
  29. package/dist/emit/d1-safety-pass.js +15 -45
  30. package/dist/emit/d1-safety-pass.js.map +1 -1
  31. package/dist/emit/postgres.d.ts.map +1 -1
  32. package/dist/emit/postgres.js +47 -4
  33. package/dist/emit/postgres.js.map +1 -1
  34. package/dist/emit/sqlite.d.ts.map +1 -1
  35. package/dist/emit/sqlite.js +22 -0
  36. package/dist/emit/sqlite.js.map +1 -1
  37. package/dist/errors.d.ts.map +1 -1
  38. package/dist/errors.js +4 -0
  39. package/dist/errors.js.map +1 -1
  40. package/dist/expected-schema.d.ts.map +1 -1
  41. package/dist/expected-schema.js +114 -5
  42. package/dist/expected-schema.js.map +1 -1
  43. package/dist/index.d.ts +13 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +13 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/introspect/d1.d.ts.map +1 -1
  48. package/dist/introspect/d1.js +1 -0
  49. package/dist/introspect/d1.js.map +1 -1
  50. package/dist/introspect/postgres.d.ts.map +1 -1
  51. package/dist/introspect/postgres.js +38 -2
  52. package/dist/introspect/postgres.js.map +1 -1
  53. package/dist/introspect/sqlite.d.ts.map +1 -1
  54. package/dist/introspect/sqlite.js +13 -2
  55. package/dist/introspect/sqlite.js.map +1 -1
  56. package/dist/snapshot/checksum.d.ts +10 -0
  57. package/dist/snapshot/checksum.d.ts.map +1 -0
  58. package/dist/snapshot/checksum.js +14 -0
  59. package/dist/snapshot/checksum.js.map +1 -0
  60. package/dist/snapshot/plan.d.ts +25 -0
  61. package/dist/snapshot/plan.d.ts.map +1 -0
  62. package/dist/snapshot/plan.js +30 -0
  63. package/dist/snapshot/plan.js.map +1 -0
  64. package/dist/snapshot/serialize.d.ts +10 -0
  65. package/dist/snapshot/serialize.d.ts.map +1 -0
  66. package/dist/snapshot/serialize.js +63 -0
  67. package/dist/snapshot/serialize.js.map +1 -0
  68. package/dist/snapshot/store.d.ts +12 -0
  69. package/dist/snapshot/store.d.ts.map +1 -0
  70. package/dist/snapshot/store.js +32 -0
  71. package/dist/snapshot/store.js.map +1 -0
  72. package/dist/sql/split-statements.d.ts +12 -0
  73. package/dist/sql/split-statements.d.ts.map +1 -0
  74. package/dist/sql/split-statements.js +112 -0
  75. package/dist/sql/split-statements.js.map +1 -0
  76. package/dist/sql-type.d.ts +2 -0
  77. package/dist/sql-type.d.ts.map +1 -1
  78. package/dist/sql-type.js +2 -0
  79. package/dist/sql-type.js.map +1 -1
  80. package/dist/types.d.ts +36 -5
  81. package/dist/types.d.ts.map +1 -1
  82. package/dist/verify/replay.d.ts +25 -0
  83. package/dist/verify/replay.d.ts.map +1 -0
  84. package/dist/verify/replay.js +25 -0
  85. package/dist/verify/replay.js.map +1 -0
  86. package/dist/view-sql-compare.d.ts +8 -0
  87. package/dist/view-sql-compare.d.ts.map +1 -0
  88. package/dist/view-sql-compare.js +44 -0
  89. package/dist/view-sql-compare.js.map +1 -0
  90. package/package.json +2 -2
  91. package/src/apply/apply.ts +340 -0
  92. package/src/apply/ledger.ts +241 -0
  93. package/src/check-expr-compare.ts +49 -0
  94. package/src/diff/index.ts +59 -15
  95. package/src/diff/status.ts +3 -0
  96. package/src/drift/classify.ts +56 -0
  97. package/src/drift/drift.ts +66 -0
  98. package/src/emit/d1-safety-pass.ts +16 -45
  99. package/src/emit/postgres.ts +47 -4
  100. package/src/emit/sqlite.ts +22 -0
  101. package/src/errors.ts +4 -0
  102. package/src/expected-schema.ts +124 -4
  103. package/src/index.ts +44 -0
  104. package/src/introspect/d1.ts +1 -0
  105. package/src/introspect/postgres.ts +38 -4
  106. package/src/introspect/sqlite.ts +13 -3
  107. package/src/snapshot/checksum.ts +15 -0
  108. package/src/snapshot/plan.ts +53 -0
  109. package/src/snapshot/serialize.ts +81 -0
  110. package/src/snapshot/store.ts +33 -0
  111. package/src/sql/split-statements.ts +115 -0
  112. package/src/sql-type.ts +3 -0
  113. package/src/types.ts +26 -9
  114. package/src/verify/replay.ts +43 -0
  115. package/src/view-sql-compare.ts +46 -0
@@ -26,6 +26,8 @@ export type SqlType = {
26
26
  withTimezone: boolean;
27
27
  } | {
28
28
  kind: "date";
29
+ } | {
30
+ kind: "time";
29
31
  } | {
30
32
  kind: "json";
31
33
  } | {
@@ -1 +1 @@
1
- {"version":3,"file":"sql-type.d.ts","sourceRoot":"","sources":["../src/sql-type.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,EAAE,GAAG,EAAE,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,sCAAsC;AACtC,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAsB7D;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,CAwC9D"}
1
+ {"version":3,"file":"sql-type.d.ts","sourceRoot":"","sources":["../src/sql-type.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,EAAE,GAAG,EAAE,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,sCAAsC;AACtC,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAuB7D;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,CAyC9D"}
package/dist/sql-type.js CHANGED
@@ -17,6 +17,7 @@ export function sqlTypeEquals(a, b) {
17
17
  case "real4":
18
18
  case "boolean":
19
19
  case "date":
20
+ case "time":
20
21
  case "json":
21
22
  case "blob":
22
23
  case "uuid":
@@ -68,6 +69,7 @@ export function isWidening(from, to) {
68
69
  case "real4":
69
70
  case "boolean":
70
71
  case "date":
72
+ case "time":
71
73
  case "json":
72
74
  case "blob":
73
75
  case "uuid":
@@ -1 +1 @@
1
- {"version":3,"file":"sql-type.js","sourceRoot":"","sources":["../src/sql-type.ts"],"names":[],"mappings":"AAoBA,sCAAsC;AACtC,MAAM,UAAU,aAAa,CAAC,CAAU,EAAE,CAAU;IAClD,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACpC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,CAAC,CAAC,SAAS,KAAM,CAAwC,CAAC,SAAS,CAAC;QAC7E,KAAK,SAAS;YACZ,OAAO,CAAC,CAAC,IAAI,KAAM,CAA2C,CAAC,IAAI,CAAC;QACtE,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,EAAE,GAAG,CAA0C,CAAC;YACtD,OAAO,CAAC,CAAC,SAAS,KAAK,EAAE,CAAC,SAAS,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,KAAK,CAAC;QAC9D,CAAC;QACD,KAAK,WAAW;YACd,OAAO,CAAC,CAAC,YAAY,KAAM,CAA6C,CAAC,YAAY,CAAC;QACxF,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,SAAS,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,IAAa,EAAE,EAAW;IACnD,IAAI,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC,CAAO,2BAA2B;IAE1E,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,CAAC,GAAG,EAAwC,CAAC;YACnD,6BAA6B;YAC7B,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC;YAC/C,gCAAgC;YAChC,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAC;YAC3C,uCAAuC;YACvC,OAAO,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC;QACvC,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,CAAC,GAAG,EAA2C,CAAC;YACtD,OAAO,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC;QAC7B,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,CAAC,GAAG,EAA2C,CAAC;YACtD,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;YAC/B,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;YAC3B,MAAM,EAAE,GAAG,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC;YAC5B,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;YACxB,6DAA6D;YAC7D,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACzD,CAAC;QACD,sFAAsF;QACtF,oFAAoF;QACpF,qFAAqF;QACrF,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,SAAS,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,WAAW;YACd,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"sql-type.js","sourceRoot":"","sources":["../src/sql-type.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -23,6 +23,7 @@ export interface TableDescriptor {
23
23
  columns: ColumnDescriptor[];
24
24
  indexes: IndexDescriptor[];
25
25
  foreignKeys: FkDescriptor[];
26
+ checks: CheckDescriptor[];
26
27
  primaryKey: string[];
27
28
  /**
28
29
  * Human-readable description threaded from entity `@description`.
@@ -55,6 +56,12 @@ export interface IndexDescriptor {
55
56
  columns: string[];
56
57
  unique: boolean;
57
58
  }
59
+ export interface CheckDescriptor {
60
+ /** Constraint name, e.g. `<table>_<column>_chk`. Diff/identity key. */
61
+ name: string;
62
+ /** The boolean SQL expression, e.g. `status IN ('OPEN','CLOSED')`. */
63
+ expression: string;
64
+ }
58
65
  export interface FkDescriptor {
59
66
  name: string;
60
67
  columns: string[];
@@ -69,11 +76,17 @@ export interface ViewDescriptor {
69
76
  /** Same semantics as TableDescriptor.schema. */
70
77
  schema?: string;
71
78
  /**
72
- * View body: everything between `CREATE VIEW <name> AS` and the trailing `;`
73
- * (the SELECT clause through the FROM/WHERE/GROUP-BY tail). Populated by
74
- * `buildExpectedSchema` from projection metadata; omitted by introspect
75
- * (body-level comparison isn't implemented yet diff matches by name only,
76
- * so a body change does NOT trigger replace-view today).
79
+ * View definition SQL.
80
+ *
81
+ * On the EXPECTED side (`buildExpectedSchema` / `buildExpectedViews`) this is
82
+ * the view body the SELECT clause through the FROM/WHERE/GROUP-BY tail.
83
+ *
84
+ * On the ACTUAL side (`introspect`) this is whatever the DB catalog stores:
85
+ * sqlite's `sqlite_master.sql` is the full `CREATE VIEW <name> AS <body>`
86
+ * statement, while Postgres' `information_schema.views.view_definition` is the
87
+ * body only. `diff`'s view-body comparator normalizes both sides (strips any
88
+ * leading `CREATE VIEW ... AS`, collapses whitespace) before comparing, so a
89
+ * body change triggers a `replace-view`.
77
90
  */
78
91
  sql?: string;
79
92
  }
@@ -97,6 +110,7 @@ export type Change = {
97
110
  kind: "drop-table";
98
111
  table: string;
99
112
  schema?: string;
113
+ restore?: TableDescriptor;
100
114
  status: ChangeStatus;
101
115
  } | {
102
116
  kind: "rename-table";
@@ -115,6 +129,7 @@ export type Change = {
115
129
  table: string;
116
130
  schema?: string;
117
131
  column: string;
132
+ restore?: ColumnDescriptor;
118
133
  status: ChangeStatus;
119
134
  } | {
120
135
  kind: "rename-column";
@@ -158,6 +173,7 @@ export type Change = {
158
173
  table: string;
159
174
  schema?: string;
160
175
  index: string;
176
+ restore?: IndexDescriptor;
161
177
  status: ChangeStatus;
162
178
  } | {
163
179
  kind: "add-fk";
@@ -170,6 +186,20 @@ export type Change = {
170
186
  table: string;
171
187
  schema?: string;
172
188
  fk: string;
189
+ restore?: FkDescriptor;
190
+ status: ChangeStatus;
191
+ } | {
192
+ kind: "add-check";
193
+ table: string;
194
+ schema?: string;
195
+ check: CheckDescriptor;
196
+ status: ChangeStatus;
197
+ } | {
198
+ kind: "drop-check";
199
+ table: string;
200
+ schema?: string;
201
+ check: string;
202
+ restore?: CheckDescriptor;
173
203
  status: ChangeStatus;
174
204
  } | {
175
205
  kind: "create-view";
@@ -199,6 +229,7 @@ export interface AllowOptions {
199
229
  typeChange?: boolean;
200
230
  dropIndex?: boolean;
201
231
  dropFk?: boolean;
232
+ dropCheck?: boolean;
202
233
  /** Existing data must satisfy NOT NULL; diff cannot verify this. */
203
234
  nullableToNotNull?: boolean;
204
235
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { Kysely } from "kysely";
2
+ import { type DriftClassification } from "../drift/classify.js";
3
+ import type { Dialect, SchemaSnapshot } from "../types.js";
4
+ export interface VerifyReplayArgs {
5
+ /** A FRESH, throwaway database. Replay applies every migration into it from empty. */
6
+ db: Kysely<Record<string, unknown>>;
7
+ dialect: Extract<Dialect, "postgres" | "sqlite">;
8
+ /** Directory holding the committed `<timestamp>-<slug>/up.sql` migrations. */
9
+ migrationsDir: string;
10
+ /** The committed snapshot the migrations are expected to reproduce. */
11
+ snapshot: SchemaSnapshot;
12
+ }
13
+ export interface VerifyReplayResult extends DriftClassification {
14
+ /** True when the replayed schema matches the snapshot (no drift, no unmanaged). */
15
+ ok: boolean;
16
+ }
17
+ /**
18
+ * Replay all committed migrations into a fresh database, introspect the result,
19
+ * and compare it to the committed snapshot. A non-empty `drift`/`unmanaged` means
20
+ * the migrations-as-applied diverge from the snapshot — e.g. a hand-edited up.sql
21
+ * that changed structure the metadata-derived snapshot doesn't know about. The
22
+ * ledger sidecar table is excluded from the comparison.
23
+ */
24
+ export declare function verifyReplay(args: VerifyReplayArgs): Promise<VerifyReplayResult>;
25
+ //# sourceMappingURL=replay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/verify/replay.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIrC,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,WAAW,gBAAgB;IAC/B,sFAAsF;IACtF,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAC;IACjD,8EAA8E;IAC9E,aAAa,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC7D,mFAAmF;IACnF,EAAE,EAAE,OAAO,CAAC;CACb;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAYtF"}
@@ -0,0 +1,25 @@
1
+ import { applyPending } from "../apply/apply.js";
2
+ import { MIGRATIONS_TABLE } from "../apply/ledger.js";
3
+ import { introspect } from "../introspect/index.js";
4
+ import { driftAgainstSnapshot } from "../drift/classify.js";
5
+ /**
6
+ * Replay all committed migrations into a fresh database, introspect the result,
7
+ * and compare it to the committed snapshot. A non-empty `drift`/`unmanaged` means
8
+ * the migrations-as-applied diverge from the snapshot — e.g. a hand-edited up.sql
9
+ * that changed structure the metadata-derived snapshot doesn't know about. The
10
+ * ledger sidecar table is excluded from the comparison.
11
+ */
12
+ export async function verifyReplay(args) {
13
+ await applyPending(args.db, args.migrationsDir, { dryRun: false, dialect: args.dialect });
14
+ const introspected = await introspect(args.db, args.dialect);
15
+ const actual = {
16
+ ...introspected,
17
+ tables: introspected.tables.filter((t) => t.name !== MIGRATIONS_TABLE),
18
+ };
19
+ const classification = await driftAgainstSnapshot(args.snapshot, actual, args.dialect);
20
+ return {
21
+ ...classification,
22
+ ok: classification.drift.length === 0 && classification.unmanaged.length === 0,
23
+ };
24
+ }
25
+ //# sourceMappingURL=replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/verify/replay.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAA4B,MAAM,sBAAsB,CAAC;AAkBtF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAsB;IACvD,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1F,MAAM,YAAY,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAmB;QAC7B,GAAG,YAAY;QACf,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC;KACvE,CAAC;IACF,MAAM,cAAc,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACvF,OAAO;QACL,GAAG,cAAc;QACjB,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,cAAc,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;KAC/E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Collapse a view definition (full `CREATE VIEW ... AS body` OR a bare body)
3
+ * to a canonical, comparable string. Whitespace-, case-, and wrapper-insensitive.
4
+ */
5
+ export declare function normalizeViewSql(sql: string): string;
6
+ /** True when two view definitions are equivalent after normalization. */
7
+ export declare function viewSqlEquals(a: string | undefined, b: string | undefined): boolean;
8
+ //# sourceMappingURL=view-sql-compare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-sql-compare.d.ts","sourceRoot":"","sources":["../src/view-sql-compare.ts"],"names":[],"mappings":"AA4BA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOpD;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGnF"}
@@ -0,0 +1,44 @@
1
+ // view-sql-compare.ts — the single shared comparator for view definition SQL.
2
+ //
3
+ // View definition SQL arrives in two shapes across the pipeline:
4
+ // - EXPECTED (buildExpectedViews): the body only — `SELECT ... FROM ...`.
5
+ // - ACTUAL (introspect): sqlite's sqlite_master.sql is the full
6
+ // `CREATE VIEW <name> AS <body>`; Postgres' view_definition is body-only.
7
+ // - The CLI's readExistingViewSql synthesizes `CREATE VIEW <name> AS <body>`
8
+ // for Postgres so it matches what emitViewDdl produces.
9
+ //
10
+ // normalizeViewSql reduces any of these to a comparable canonical form so the
11
+ // diff (expected vs introspected) and the CLI (emitted DDL vs existing DDL) use
12
+ // ONE comparison. It strips a leading `CREATE [OR REPLACE] VIEW <name> AS`,
13
+ // collapses runs of whitespace to a single space, drops a trailing `;`, and
14
+ // lower-cases — view-body drift should be classified by structure, not by
15
+ // incidental whitespace/case/wrapper differences.
16
+ //
17
+ // CAVEAT (accepted tradeoff): lower-casing makes keyword/identifier comparison
18
+ // case-insensitive but can mask a difference that lives ONLY in a case-sensitive
19
+ // string literal in the body (e.g. `WHERE status = 'Active'` vs `'active'`) —
20
+ // such a change would NOT be flagged as drift. Acceptable for generated
21
+ // aggregate/passthrough projections (no literals); revisit if hand-authored
22
+ // views with case-sensitive literals become a drift concern. The name regex
23
+ // matches one whitespace/`(`-free token, so a quoted view name containing a
24
+ // space would not strip cleanly — also a non-issue for generated identifiers.
25
+ const CREATE_VIEW_PREFIX = /^\s*create\s+(?:or\s+replace\s+)?(?:temp(?:orary)?\s+)?view\s+(?:if\s+not\s+exists\s+)?[^\s(]+(?:\s*\([^)]*\))?\s+as\s+/i;
26
+ /**
27
+ * Collapse a view definition (full `CREATE VIEW ... AS body` OR a bare body)
28
+ * to a canonical, comparable string. Whitespace-, case-, and wrapper-insensitive.
29
+ */
30
+ export function normalizeViewSql(sql) {
31
+ return sql
32
+ .replace(CREATE_VIEW_PREFIX, "")
33
+ .replace(/\s+/g, " ")
34
+ .replace(/;\s*$/, "")
35
+ .trim()
36
+ .toLowerCase();
37
+ }
38
+ /** True when two view definitions are equivalent after normalization. */
39
+ export function viewSqlEquals(a, b) {
40
+ if (a === undefined || b === undefined)
41
+ return false;
42
+ return normalizeViewSql(a) === normalizeViewSql(b);
43
+ }
44
+ //# sourceMappingURL=view-sql-compare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-sql-compare.js","sourceRoot":"","sources":["../src/view-sql-compare.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,iEAAiE;AACjE,4EAA4E;AAC5E,kEAAkE;AAClE,8EAA8E;AAC9E,+EAA+E;AAC/E,4DAA4D;AAC5D,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAC5E,4EAA4E;AAC5E,0EAA0E;AAC1E,kDAAkD;AAClD,EAAE;AACF,+EAA+E;AAC/E,iFAAiF;AACjF,8EAA8E;AAC9E,wEAAwE;AACxE,4EAA4E;AAC5E,4EAA4E;AAC5E,4EAA4E;AAC5E,8EAA8E;AAE9E,MAAM,kBAAkB,GACtB,0HAA0H,CAAC;AAE7H;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,GAAG;SACP,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAC/B,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;SACpB,IAAI,EAAE;SACN,WAAW,EAAE,CAAC;AACnB,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,aAAa,CAAC,CAAqB,EAAE,CAAqB;IACxE,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACrD,OAAO,gBAAgB,CAAC,CAAC,CAAC,KAAK,gBAAgB,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/migrate-ts",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Schema migration tooling for MetaObjects: diff metadata vs DB and emit SQL for Postgres and SQLite.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@iarna/toml": "^2.2.5",
37
- "@metaobjectsdev/metadata": "0.8.1"
37
+ "@metaobjectsdev/metadata": "0.9.0"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "kysely": ">=0.27.0"
@@ -0,0 +1,340 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readdir, readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { type Kysely, type Transaction, sql } from "kysely";
5
+ import {
6
+ appliedRecords,
7
+ DEFAULT_LEDGER_SCHEMA,
8
+ deleteApplied,
9
+ ensureLedger,
10
+ type LedgerDialect,
11
+ type LedgerOptions,
12
+ MIGRATIONS_TABLE,
13
+ recordApplied,
14
+ } from "./ledger.js";
15
+ import { splitSqlStatements } from "../sql/split-statements.js";
16
+
17
+ // Re-exported here for back-compat: `splitSqlStatements` historically lived in
18
+ // this module. Its canonical home is now ../sql/split-statements.js (shared with
19
+ // the D1 safety pass), but consumers importing from apply.js keep working.
20
+ export { splitSqlStatements };
21
+
22
+ /** The per-migration up-SQL filename, shared with writeMigration's layout. */
23
+ const UP_SQL = "up.sql";
24
+ /** The per-migration down-SQL filename, shared with writeMigration's layout. */
25
+ const DOWN_SQL = "down.sql";
26
+
27
+ export interface ApplyPendingOptions {
28
+ /** When true, compute + return the plan but apply nothing. */
29
+ dryRun: boolean;
30
+ /**
31
+ * Target dialect. Decides ledger schema-qualification (pg) and whether the
32
+ * Postgres advisory lock is taken. Defaults to `sqlite` (no schema, no lock)
33
+ * to preserve the original single-DB behavior for callers that omit it.
34
+ */
35
+ dialect?: LedgerDialect;
36
+ /** Multi-tenant ledger location + advisory-lock name. Defaults preserve current behavior. */
37
+ ledger?: LedgerOptions;
38
+ }
39
+
40
+ export interface ApplyPendingResult {
41
+ /** Migration names that were pending (not yet in the ledger), in order. */
42
+ pending: string[];
43
+ /** Migration names that were applied this run, in order. Empty on dryRun. */
44
+ applied: string[];
45
+ }
46
+
47
+ interface DiscoveredMigration {
48
+ /** `<timestamp>-<slug>` directory name — stable id + sort key. */
49
+ name: string;
50
+ /** Absolute path to the up.sql file. */
51
+ upPath: string;
52
+ /** Absolute path to the down.sql file (may not exist on disk). */
53
+ downPath: string;
54
+ }
55
+
56
+ /**
57
+ * Apply pending committed migration files in order, tracked by the
58
+ * migration-history ledger, transactionally.
59
+ *
60
+ * Idempotency comes from the LEDGER (skip names already recorded), NOT from
61
+ * re-diffing — so hand-authored files + data steps replay exactly once.
62
+ *
63
+ * For each pending migration (sorted by directory name), the file's SQL and a
64
+ * `recordApplied` row are run in the SAME Kysely transaction; any failure rolls
65
+ * back that file's tx, leaving it unrecorded (so a re-run retries it), and
66
+ * stops the run. Previously-applied files are checksum-compared against the
67
+ * ledger — a changed file errors (tamper guard).
68
+ */
69
+ export async function applyPending(
70
+ db: Kysely<Record<string, unknown>>,
71
+ dir: string,
72
+ opts: ApplyPendingOptions,
73
+ ): Promise<ApplyPendingResult> {
74
+ const dialect = opts.dialect ?? "sqlite";
75
+ const ledger = opts.ledger;
76
+
77
+ // Serialize concurrent applies against the same ledger with a Postgres
78
+ // session advisory lock (no-op on SQLite). Held for the whole apply duration.
79
+ return withAdvisoryLock(db, dialect, ledger, async () => {
80
+ await ensureLedger(db, dialect, ledger);
81
+ const recorded = await appliedRecords(db, dialect, ledger);
82
+
83
+ const discovered = await discoverMigrations(dir);
84
+
85
+ // Tamper guard: any already-applied migration whose current up.sql checksum
86
+ // differs from the recorded one is a hard error.
87
+ for (const m of discovered) {
88
+ const recordedChecksum = recorded.get(m.name);
89
+ if (recordedChecksum === undefined) continue;
90
+ const current = checksumOf(await readFile(m.upPath, "utf8"));
91
+ if (current !== recordedChecksum) {
92
+ throw new Error(
93
+ `migration '${m.name}' was already applied but its up.sql checksum changed ` +
94
+ `(recorded ${recordedChecksum.slice(0, 12)}…, current ${current.slice(0, 12)}…). ` +
95
+ `Applied migrations are immutable; revert the edit or author a new migration.`,
96
+ );
97
+ }
98
+ }
99
+
100
+ const pending = discovered.filter((m) => !recorded.has(m.name));
101
+ const pendingNames = pending.map((m) => m.name);
102
+
103
+ if (opts.dryRun) {
104
+ return { pending: pendingNames, applied: [] };
105
+ }
106
+
107
+ const applied: string[] = [];
108
+ for (const m of pending) {
109
+ const text = await readFile(m.upPath, "utf8");
110
+ const checksum = checksumOf(text);
111
+ // Run the file's SQL + the ledger insert in ONE transaction. A failure
112
+ // rolls the whole file back (unrecorded) and propagates — stopping the run.
113
+ await runSqlFileWithLedgerMutation(db, text, (trx) =>
114
+ recordApplied(trx, m.name, checksum, dialect, ledger),
115
+ );
116
+ applied.push(m.name);
117
+ }
118
+
119
+ return { pending: pendingNames, applied };
120
+ });
121
+ }
122
+
123
+ export interface RollbackToOptions {
124
+ /** Target dialect. Decides ledger schema-qualification + advisory lock. Defaults to `sqlite`. */
125
+ dialect?: LedgerDialect;
126
+ /** Multi-tenant ledger location + advisory-lock name. Defaults preserve current behavior. */
127
+ ledger?: LedgerOptions;
128
+ }
129
+
130
+ export interface RollbackToResult {
131
+ /** Migration names rolled back, in execution (reverse-chronological) order. */
132
+ rolledBack: string[];
133
+ }
134
+
135
+ /**
136
+ * Roll back applied migrations newer than `target` (or all, when `target` is
137
+ * `null`), in REVERSE lexical order — running each migration's `down.sql` then
138
+ * deleting its ledger row, in ONE transaction per migration. `target` is itself
139
+ * retained (only ledger names strictly-greater than it are rolled back; lexical
140
+ * = chronological given the zero-padded timestamp prefix).
141
+ *
142
+ * An empty / whitespace-only `down.sql` THROWS before that migration is
143
+ * unrecorded — data-migration downs are hand-authored and must never be
144
+ * silently skipped. `down.sql` is split with the same {@link splitSqlStatements}
145
+ * the up-path uses. Wrapped in the same Postgres session advisory lock as
146
+ * {@link applyPending} (no-op on SQLite).
147
+ */
148
+ export async function rollbackTo(
149
+ db: Kysely<Record<string, unknown>>,
150
+ dir: string,
151
+ target: string | null,
152
+ opts: RollbackToOptions = {},
153
+ ): Promise<RollbackToResult> {
154
+ const dialect = opts.dialect ?? "sqlite";
155
+ const ledger = opts.ledger;
156
+
157
+ return withAdvisoryLock(db, dialect, ledger, async () => {
158
+ await ensureLedger(db, dialect, ledger);
159
+ const recorded = await appliedRecords(db, dialect, ledger);
160
+
161
+ const discovered = await discoverMigrations(dir);
162
+ const byName = new Map(discovered.map((m) => [m.name, m]));
163
+
164
+ // Applied names strictly-greater than target (or all when target is null),
165
+ // newest-first.
166
+ const toRollback = [...recorded.keys()]
167
+ .filter((name) => target === null || compareLexical(name, target) > 0)
168
+ .sort((a, b) => compareLexical(b, a));
169
+
170
+ const rolledBack: string[] = [];
171
+ for (const name of toRollback) {
172
+ const m = byName.get(name);
173
+ if (m === undefined) {
174
+ throw new Error(
175
+ `rollback '${name}': migration directory is missing (cannot read its down.sql)`,
176
+ );
177
+ }
178
+ const downText = await readDownSql(m.downPath, name);
179
+ if (downText.trim().length === 0) {
180
+ throw new Error(
181
+ `rollback '${name}': down.sql is empty — data-migration downs must be ` +
182
+ `hand-authored, never silently skipped.`,
183
+ );
184
+ }
185
+ // Run the down SQL + the ledger delete in ONE transaction.
186
+ await runSqlFileWithLedgerMutation(db, downText, (trx) =>
187
+ deleteApplied(trx, name, dialect, ledger),
188
+ );
189
+ rolledBack.push(name);
190
+ }
191
+
192
+ return { rolledBack };
193
+ });
194
+ }
195
+
196
+ /**
197
+ * Read a migration's `down.sql`. Distinguishes a MISSING file (never authored —
198
+ * ENOENT) from a present-but-empty one: a missing down throws a "not found"
199
+ * error (a never-written down has a different cause than a deliberately-blank
200
+ * one), while genuinely-empty content falls through to the caller's empty-down
201
+ * check. Both remain hard errors; this only reports the right cause.
202
+ */
203
+ async function readDownSql(path: string, name: string): Promise<string> {
204
+ try {
205
+ return await readFile(path, "utf8");
206
+ } catch (err) {
207
+ if (isErrnoException(err) && err.code === "ENOENT") {
208
+ throw new Error(
209
+ `rollback '${name}': down.sql not found for migration '${name}' ` +
210
+ `(expected at ${path}) — data-migration downs must be hand-authored.`,
211
+ );
212
+ }
213
+ throw err;
214
+ }
215
+ }
216
+
217
+ /** Narrow an unknown caught value to a Node errno exception (has a string `code`). */
218
+ function isErrnoException(err: unknown): err is NodeJS.ErrnoException {
219
+ return (
220
+ err instanceof Error &&
221
+ typeof (err as NodeJS.ErrnoException).code === "string"
222
+ );
223
+ }
224
+
225
+ /**
226
+ * Run `body` while holding a Postgres SESSION-level advisory lock for mutual
227
+ * exclusion across concurrent applies/rollbacks against the same ledger.
228
+ *
229
+ * pg session advisory locks are per-connection, so the lock is taken on a single
230
+ * dedicated connection (`db.connection()`) held for the entire `body` duration;
231
+ * `body` still runs its own migrations via `db.transaction()` on the pool — the
232
+ * lock only needs to be held by some session for mutual exclusion. SESSION (not
233
+ * transaction) level so a `CREATE INDEX CONCURRENTLY` in a migration cannot
234
+ * deadlock against it. On SQLite (single-writer; no advisory locks) it is a
235
+ * pass-through.
236
+ */
237
+ async function withAdvisoryLock<T>(
238
+ db: Kysely<Record<string, unknown>>,
239
+ dialect: LedgerDialect,
240
+ ledger: LedgerOptions | undefined,
241
+ body: () => Promise<T>,
242
+ ): Promise<T> {
243
+ if (dialect !== "postgres") {
244
+ return body();
245
+ }
246
+ const key = advisoryKey(lockNameFor(ledger));
247
+ return db.connection().execute(async (lockConn) => {
248
+ // Bind the key as a parameter cast to bigint (a signed 64-bit int as a
249
+ // decimal string, possibly negative) — matching the runner's
250
+ // `pg_advisory_lock($1::bigint)`.
251
+ await sql`SELECT pg_advisory_lock(${key}::bigint)`.execute(lockConn);
252
+ try {
253
+ return await body();
254
+ } finally {
255
+ // Releasing the lock must NOT mask an in-flight body error: a throw out of
256
+ // `finally` would replace any pending body rejection. Log-and-swallow the
257
+ // unlock failure so the body's error (if any) propagates intact. The lock
258
+ // is session-scoped, so it is released anyway when the connection closes.
259
+ try {
260
+ await sql`SELECT pg_advisory_unlock(${key}::bigint)`.execute(lockConn);
261
+ } catch (unlockErr) {
262
+ console.warn(
263
+ `migrate-ts: failed to release advisory lock (it will be freed when the ` +
264
+ `session ends): ${String(unlockErr)}`,
265
+ );
266
+ }
267
+ }
268
+ });
269
+ }
270
+
271
+ /** Default advisory-lock name: explicit `lockName`, else `<schema>.<table>`. */
272
+ function lockNameFor(ledger: LedgerOptions | undefined): string {
273
+ if (ledger?.lockName !== undefined) return ledger.lockName;
274
+ const schema = ledger?.schema ?? DEFAULT_LEDGER_SCHEMA;
275
+ const table = ledger?.table ?? MIGRATIONS_TABLE;
276
+ return `${schema}.${table}`;
277
+ }
278
+
279
+ /** Stable 64-bit signed advisory-lock key (decimal string) from a lock name. */
280
+ function advisoryKey(name: string): string {
281
+ const hash = createHash("sha256").update(name).digest();
282
+ return hash.readBigInt64BE(0).toString();
283
+ }
284
+
285
+ /**
286
+ * Run a migration SQL file's statements followed by a ledger mutation, all in
287
+ * ONE Kysely transaction on the pool. The file is split with
288
+ * {@link splitSqlStatements} and each statement executed in order, then
289
+ * `mutateLedger` records/unrecords the migration — so the data change and its
290
+ * ledger row commit or roll back together. Any failure rolls the whole
291
+ * transaction back (leaving the ledger untouched) and propagates to the caller.
292
+ *
293
+ * Shared by both the apply (up.sql + recordApplied) and rollback
294
+ * (down.sql + deleteApplied) paths.
295
+ */
296
+ async function runSqlFileWithLedgerMutation(
297
+ db: Kysely<Record<string, unknown>>,
298
+ sqlText: string,
299
+ mutateLedger: (trx: Transaction<Record<string, unknown>>) => Promise<void>,
300
+ ): Promise<void> {
301
+ await db.transaction().execute(async (trx) => {
302
+ for (const stmt of splitSqlStatements(sqlText)) {
303
+ await sql.raw(stmt).execute(trx);
304
+ }
305
+ await mutateLedger(trx);
306
+ });
307
+ }
308
+
309
+ async function discoverMigrations(dir: string): Promise<DiscoveredMigration[]> {
310
+ let entries: { name: string; isDirectory: () => boolean }[];
311
+ try {
312
+ entries = await readdir(dir, { withFileTypes: true });
313
+ } catch {
314
+ return [];
315
+ }
316
+ const migrations: DiscoveredMigration[] = [];
317
+ for (const e of entries) {
318
+ if (!e.isDirectory()) continue;
319
+ migrations.push({
320
+ name: e.name,
321
+ upPath: join(dir, e.name, UP_SQL),
322
+ downPath: join(dir, e.name, DOWN_SQL),
323
+ });
324
+ }
325
+ // Directory names are timestamp-prefixed (`<YYYYMMDDHHMMSS>-<slug>`), so a
326
+ // plain lexical (code-unit) sort is the apply order.
327
+ migrations.sort((a, b) => compareLexical(a.name, b.name));
328
+ return migrations;
329
+ }
330
+
331
+ function checksumOf(text: string): string {
332
+ return createHash("sha256").update(text, "utf8").digest("hex");
333
+ }
334
+
335
+ /** Stable lexical (code-unit) comparison; the same ordering as `a < b`/`a > b`. */
336
+ function compareLexical(a: string, b: string): number {
337
+ if (a < b) return -1;
338
+ if (a > b) return 1;
339
+ return 0;
340
+ }