migraguard 0.6.0 → 0.6.2

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/README.md CHANGED
@@ -204,7 +204,7 @@ See [docs/expand-contract.md](docs/expand-contract.md) for the complete guide: f
204
204
  | `gate` | Evaluate deployment gate conditions |
205
205
  | `baseline` | Squash applied migrations into `schema.sql` |
206
206
 
207
- See [COMMANDS.md](COMMANDS.md) for detailed usage, options, and examples.
207
+ See [docs/commands.md](docs/commands.md) for detailed usage, options, and examples.
208
208
 
209
209
  ## CI Integration
210
210
 
@@ -542,8 +542,9 @@ No. `verify` creates a temporary shadow DB, applies migrations twice, then drops
542
542
 
543
543
  ## Detailed Documentation
544
544
 
545
- - [COMMANDS.md](COMMANDS.md) — Full command reference with options and examples
545
+ - [docs/commands.md](docs/commands.md) — Full command reference with options and examples
546
546
  - [docs/state-model.md](docs/state-model.md) — Apply/check/resolve/squash flows, INSERT-only design, regression detection
547
547
  - [docs/dag-internals.md](docs/dag-internals.md) — Dependency analysis, explicit declarations, DAG migration compatibility
548
548
  - [docs/safe-ddl.md](docs/safe-ddl.md) — Safe DDL patterns for PostgreSQL (lock timeout, CONCURRENTLY, batch backfills)
549
- - [docs/expand-contract.md](docs/expand-contract.md) — Expand/contract pattern: phased migrations, state machine, CI/CD deployment gate, TypeScript API
549
+ - [docs/expand-contract.md](docs/expand-contract.md) — Expand/contract pattern: phased migrations, state machine, CI/CD deployment gate
550
+ - [docs/typescript-api.md](docs/typescript-api.md) — TypeScript programmatic API: all commands as typed async functions
@@ -0,0 +1,165 @@
1
+ # DAG Model: Dependency Analysis Internals
2
+
3
+ ## Dependency Analysis Method
4
+
5
+ Each migration SQL is parsed into an AST using a PostgreSQL parser (`libpg_query`) to extract object creation/reference relationships and build a DAG (directed acyclic graph).
6
+
7
+ ```
8
+ Information extracted from SQL statements:
9
+
10
+ CREATE TABLE users (...)
11
+ → creates: users
12
+
13
+ ALTER TABLE users ADD COLUMN email VARCHAR(256)
14
+ → depends: users → creates: users.email
15
+
16
+ CREATE INDEX CONCURRENTLY ON users (email)
17
+ → depends: users, users.email
18
+
19
+ CREATE TABLE orders (user_id INT REFERENCES users(id))
20
+ → depends: users → creates: orders
21
+ ```
22
+
23
+ ### Extractable DDL and Dependency Types
24
+
25
+ | DDL | Creates | Depends On |
26
+ |-----|---------|------------|
27
+ | `CREATE TABLE` | table | none (referenced tables if REFERENCES present) |
28
+ | `ALTER TABLE ADD COLUMN` | column | table |
29
+ | `ALTER TABLE ADD CONSTRAINT` | constraint | table, columns, referenced tables |
30
+ | `CREATE INDEX` | index | table, columns |
31
+ | `CREATE VIEW` | view | referenced tables |
32
+ | `CREATE FUNCTION` | function | referenced tables (requires body analysis) |
33
+ | `DROP *` | none | target object |
34
+
35
+ ### Limitations of Auto-Extraction
36
+
37
+ | Case | Auto-extraction | Workaround |
38
+ |------|----------------|------------|
39
+ | `CREATE TABLE` / `ALTER TABLE` / `CREATE INDEX` / `CREATE VIEW` | ✅ extractable | — |
40
+ | Table references inside `CREATE FUNCTION` body | ⚠️ partial | Static analysis of function body SQL, but dynamic SQL (`EXECUTE format(...)` etc.) is undetectable. Use explicit declarations |
41
+ | DDL inside `DO $$ ... $$` blocks | ❌ undetectable | Explicit declaration required |
42
+ | Dynamic SQL (`EXECUTE`, variable expansion) | ❌ undetectable | Explicit declaration required |
43
+ | Implicit schema references via `search_path` | ❌ undetectable | Explicit declaration required |
44
+ | Business-logic ordering dependencies (data dependencies) | ❌ out of scope | Explicit declaration required |
45
+
46
+ When auto-extraction fails to detect dependencies, `check` will pass without warning. Add explicit declarations when in doubt.
47
+
48
+ ## Explicit Dependency Declaration
49
+
50
+ Dependencies that cannot be auto-extracted are explicitly declared via SQL file comments:
51
+
52
+ ```sql
53
+ -- migraguard:depends-on 20260228_120000__create_users_table.sql
54
+
55
+ SET lock_timeout = '5s';
56
+ ...
57
+ ```
58
+
59
+ Or declared in `migraguard.config.json`:
60
+
61
+ ```json
62
+ {
63
+ "dependencies": {
64
+ "20260301_093000__backfill_user_status.sql": [
65
+ "20260228_120000__add_user_status_column.sql"
66
+ ]
67
+ }
68
+ }
69
+ ```
70
+
71
+ Auto-extracted and explicit declarations are merged to build the final DAG. Explicit declarations are **composed as additional dependencies** (they cannot reduce dependencies). The final DAG is the union of both.
72
+
73
+ ## Impact on check and apply
74
+
75
+ In the dependency tree model, "latest file (tail)" is replaced by "leaf node (a file that no other file depends on)."
76
+
77
+ ```
78
+ Linear model: A → B → C → [D]
79
+ ↑ editable (tail only)
80
+
81
+ DAG model:
82
+ A (locked — B, C depend on it)
83
+ / \
84
+ B C (locked — D, E depend on them)
85
+ | \
86
+ [D] [E] ← editable (leaf nodes)
87
+
88
+ CI (check):
89
+ - Non-leaf node modified → error
90
+ - Leaf node modified → allowed
91
+ - New file depends on existing leaf → that leaf transitions to locked
92
+ ```
93
+
94
+ **apply in DAG mode**:
95
+ - Files are applied in topological sort order (dependencies first)
96
+ - Independent files have no ordering constraint
97
+ - On failure: only files depending on the failed file are blocked; independent files are unaffected
98
+
99
+ ```
100
+ Example: D fails
101
+
102
+ A
103
+ / \
104
+ B C
105
+ | \
106
+ [D] E ← independent of D, so apply proceeds
107
+
108
+ D's failure does not block E's release
109
+ ```
110
+
111
+ ## Squash in DAG Mode
112
+
113
+ New files are automatically split into connected components (groups) based on dependencies. Squash is performed per group; independent DDL remains as individual files.
114
+
115
+ ```
116
+ Before squash:
117
+ 20260308_100000__create_follows.sql (new — depends on users)
118
+ 20260308_110000__add_follow_index.sql (new — depends on follows)
119
+ 20260309_100000__create_notifications.sql (new — depends on users, independent of follows)
120
+
121
+ After squash:
122
+ 20260308_110000__create_follows_and_add_follow_index.sql (dependency chain merged)
123
+ 20260309_100000__create_notifications.sql (independent — unchanged)
124
+ ```
125
+
126
+ Within each group, files are concatenated in ascending timestamp order, guaranteeing that dependencies precede dependents.
127
+
128
+ ## Benefits for Large-Scale Systems
129
+
130
+ | Constraint | Linear Model | Dependency Tree Model |
131
+ |------------|-------------|----------------------|
132
+ | Concurrently modifiable files | 1 (tail only) | Number of leaf nodes |
133
+ | Parallel releases | Not possible | Independent branches can be released in parallel |
134
+ | Error blast radius | All subsequent files blocked | Only dependent files blocked |
135
+ | Multi-team work | Serialized | Parallel work possible for independent tables |
136
+
137
+ ## DAG Migration Compatibility Policy
138
+
139
+ When migrating from the linear model to the dependency tree model, compatibility with existing schema_migrations is maintained.
140
+
141
+ ### Migration Steps
142
+
143
+ 1. **Retain existing schema_migrations as-is**: Records from the linear model are treated as files with "implicitly fully-serial dependencies"
144
+ 2. **Migration point marker**: Add `"model": "dag"` flag to metadata.json. Files before this flag use linear ordering; files after use DAG analysis
145
+ 3. **Backward compatibility**: DAG-aware migraguard can read linear model metadata.json. The reverse (downgrade from DAG to linear) is not supported
146
+
147
+ ```
148
+ metadata.json example:
149
+
150
+ {
151
+ "model": "dag",
152
+ "modelSince": "20260401_000000__first_dag_migration.sql",
153
+ "migrations": [
154
+ {"file": "20260301_...", "checksum": "aaa"}, ← linear model era (fully serial)
155
+ {"file": "20260302_...", "checksum": "bbb"}, ← linear model era
156
+ {"file": "20260401_...", "checksum": "ccc"} ← DAG model (dependency analysis)
157
+ ]
158
+ }
159
+ ```
160
+
161
+ ### check / apply Behavior at Boundary
162
+
163
+ - Files before `modelSince`: Checked linearly by timestamp as before
164
+ - Files after `modelSince`: Leaf node determination and topological sort via DAG analysis
165
+ - Boundary: The `modelSince` file implicitly depends on all prior files (inherits the linear model's final state)