ddlforge 0.1.1 → 0.2.1
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 +519 -198
- package/dist/src/cli.d.ts +25 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +253 -2
- package/dist/src/cli.js.map +1 -1
- package/dist/src/engine/analyzer.d.ts.map +1 -1
- package/dist/src/engine/analyzer.js.map +1 -1
- package/dist/src/lexer/sqlTokenizer.d.ts.map +1 -1
- package/dist/src/lexer/sqlTokenizer.js.map +1 -1
- package/dist/src/reporters/json.js.map +1 -1
- package/dist/src/reporters/terminal.js.map +1 -1
- package/dist/src/runner/backoff.d.ts +53 -0
- package/dist/src/runner/backoff.d.ts.map +1 -0
- package/dist/src/runner/backoff.js +65 -0
- package/dist/src/runner/backoff.js.map +1 -0
- package/dist/src/runner/executor.d.ts +106 -0
- package/dist/src/runner/executor.d.ts.map +1 -0
- package/dist/src/runner/executor.js +444 -0
- package/dist/src/runner/executor.js.map +1 -0
- package/dist/src/runner/locksMonitor.d.ts +85 -0
- package/dist/src/runner/locksMonitor.d.ts.map +1 -0
- package/dist/src/runner/locksMonitor.js +183 -0
- package/dist/src/runner/locksMonitor.js.map +1 -0
- package/package.json +7 -3
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge - Background lock-queue monitor for PostgreSQL execution supervisor.
|
|
3
|
+
*
|
|
4
|
+
* During DDL execution the monitor periodically queries pg_locks and
|
|
5
|
+
* pg_stat_activity to detect when other backends are queuing behind the
|
|
6
|
+
* migration's exclusive lock. If the queue depth exceeds a configurable
|
|
7
|
+
* threshold the monitor cancels the migration backend via pg_cancel_backend()
|
|
8
|
+
* and sets the `avalanche` flag so the executor can surface a clear error.
|
|
9
|
+
*
|
|
10
|
+
* Design notes
|
|
11
|
+
* ────────────
|
|
12
|
+
* • Uses a separate Client connection so it never shares the migration
|
|
13
|
+
* transaction (avoids deadlock on the monitor itself).
|
|
14
|
+
* • All monitor queries use SHORT local timeouts so a hung Postgres cannot
|
|
15
|
+
* also jam the monitor.
|
|
16
|
+
* • The monitor resolves its returned promise with a MonitorResult when it
|
|
17
|
+
* self-terminates; callers must await that to detect the avalanche flag.
|
|
18
|
+
*/
|
|
19
|
+
export interface MonitorOptions {
|
|
20
|
+
/** PID of the migration backend being guarded */
|
|
21
|
+
migrationPid: number;
|
|
22
|
+
/**
|
|
23
|
+
* Factory that returns a fresh client already connected to the database.
|
|
24
|
+
* The monitor will call end() on it when done.
|
|
25
|
+
*/
|
|
26
|
+
clientFactory: () => Promise<MonitorClient>;
|
|
27
|
+
/** Milliseconds between each poll (default: 500 ms) */
|
|
28
|
+
pollIntervalMs?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Number of waiting backends that triggers a cancellation
|
|
31
|
+
* (default: 1 – i.e. cancel as soon as even one query is blocked).
|
|
32
|
+
*/
|
|
33
|
+
queueThreshold?: number;
|
|
34
|
+
/** AbortSignal: when aborted the monitor stops without cancelling */
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Minimal pg Client interface required by the monitor.
|
|
39
|
+
* Using a structural type allows tests to inject simple mocks.
|
|
40
|
+
*/
|
|
41
|
+
export interface MonitorClient {
|
|
42
|
+
query(sql: string, params?: unknown[]): Promise<{
|
|
43
|
+
rows: Record<string, unknown>[];
|
|
44
|
+
}>;
|
|
45
|
+
end(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
export interface MonitorResult {
|
|
48
|
+
/** True when the monitor detected a queue avalanche and cancelled the backend */
|
|
49
|
+
avalanche: boolean;
|
|
50
|
+
/** Number of waiting backends detected at cancellation time */
|
|
51
|
+
blockedCount: number;
|
|
52
|
+
/** ISO timestamp at which cancellation was issued (empty if no cancellation) */
|
|
53
|
+
cancelledAt: string;
|
|
54
|
+
/** Reason the monitor stopped */
|
|
55
|
+
stopReason: 'aborted' | 'avalanche' | 'error' | 'external-stop';
|
|
56
|
+
}
|
|
57
|
+
export interface BlockedBackend {
|
|
58
|
+
waiterPid: number;
|
|
59
|
+
waiterQuery: string;
|
|
60
|
+
waiterState: string;
|
|
61
|
+
waitEventType: string;
|
|
62
|
+
waitEvent: string;
|
|
63
|
+
blockerMode: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Starts a non-blocking background lock-queue monitor.
|
|
67
|
+
*
|
|
68
|
+
* Returns a Promise<MonitorResult> that settles when the monitor self-stops
|
|
69
|
+
* (either via the AbortSignal, an avalanche cancellation, or an internal error).
|
|
70
|
+
*
|
|
71
|
+
* Also returns a `stop()` function that callers use to gracefully terminate
|
|
72
|
+
* the monitor after successful execution.
|
|
73
|
+
*/
|
|
74
|
+
export declare function startLockMonitor(options: MonitorOptions): {
|
|
75
|
+
result: Promise<MonitorResult>;
|
|
76
|
+
stop: () => void;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Determines whether a Postgres error code represents a lock-related failure.
|
|
80
|
+
*
|
|
81
|
+
* 55P03 — lock_not_available (SET LOCAL lock_timeout exceeded)
|
|
82
|
+
* 57014 — query_canceled (SET LOCAL statement_timeout exceeded, or pg_cancel_backend)
|
|
83
|
+
*/
|
|
84
|
+
export declare function isLockError(code: string): boolean;
|
|
85
|
+
//# sourceMappingURL=locksMonitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locksMonitor.d.ts","sourceRoot":"","sources":["../../../src/runner/locksMonitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,MAAM,WAAW,cAAc;IAC7B,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,aAAa,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5C,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAC,CAAC;IACrF,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,UAAU,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,eAAe,CAAC;CACjE;AAqCD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG;IACzD,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/B,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CA0HA;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge - Background lock-queue monitor for PostgreSQL execution supervisor.
|
|
3
|
+
*
|
|
4
|
+
* During DDL execution the monitor periodically queries pg_locks and
|
|
5
|
+
* pg_stat_activity to detect when other backends are queuing behind the
|
|
6
|
+
* migration's exclusive lock. If the queue depth exceeds a configurable
|
|
7
|
+
* threshold the monitor cancels the migration backend via pg_cancel_backend()
|
|
8
|
+
* and sets the `avalanche` flag so the executor can surface a clear error.
|
|
9
|
+
*
|
|
10
|
+
* Design notes
|
|
11
|
+
* ────────────
|
|
12
|
+
* • Uses a separate Client connection so it never shares the migration
|
|
13
|
+
* transaction (avoids deadlock on the monitor itself).
|
|
14
|
+
* • All monitor queries use SHORT local timeouts so a hung Postgres cannot
|
|
15
|
+
* also jam the monitor.
|
|
16
|
+
* • The monitor resolves its returned promise with a MonitorResult when it
|
|
17
|
+
* self-terminates; callers must await that to detect the avalanche flag.
|
|
18
|
+
*/
|
|
19
|
+
/** SQL that detects backends queuing on locks held/waited by our migration PID */
|
|
20
|
+
const BLOCKED_QUERY_SQL = `
|
|
21
|
+
SELECT COUNT(*) AS blocked_count
|
|
22
|
+
FROM pg_locks blocker
|
|
23
|
+
JOIN pg_locks waiter
|
|
24
|
+
ON waiter.relation = blocker.relation
|
|
25
|
+
AND waiter.locktype = blocker.locktype
|
|
26
|
+
AND waiter.pid <> blocker.pid
|
|
27
|
+
WHERE blocker.pid = $1
|
|
28
|
+
AND blocker.granted = TRUE
|
|
29
|
+
AND waiter.granted = FALSE
|
|
30
|
+
`;
|
|
31
|
+
/** SQL that also captures pg_stat_activity info for richer diagnostics */
|
|
32
|
+
const BLOCKED_QUERY_WITH_ACTIVITY_SQL = `
|
|
33
|
+
SELECT
|
|
34
|
+
waiter_act.pid AS waiter_pid,
|
|
35
|
+
waiter_act.query AS waiter_query,
|
|
36
|
+
waiter_act.state AS waiter_state,
|
|
37
|
+
waiter_act.wait_event_type AS wait_event_type,
|
|
38
|
+
waiter_act.wait_event AS wait_event,
|
|
39
|
+
blocker.mode AS blocker_mode
|
|
40
|
+
FROM pg_locks blocker
|
|
41
|
+
JOIN pg_locks waiter
|
|
42
|
+
ON waiter.relation = blocker.relation
|
|
43
|
+
AND waiter.locktype = blocker.locktype
|
|
44
|
+
AND waiter.pid <> blocker.pid
|
|
45
|
+
JOIN pg_stat_activity waiter_act
|
|
46
|
+
ON waiter_act.pid = waiter.pid
|
|
47
|
+
WHERE blocker.pid = $1
|
|
48
|
+
AND blocker.granted = TRUE
|
|
49
|
+
AND waiter.granted = FALSE
|
|
50
|
+
LIMIT 20
|
|
51
|
+
`;
|
|
52
|
+
/**
|
|
53
|
+
* Starts a non-blocking background lock-queue monitor.
|
|
54
|
+
*
|
|
55
|
+
* Returns a Promise<MonitorResult> that settles when the monitor self-stops
|
|
56
|
+
* (either via the AbortSignal, an avalanche cancellation, or an internal error).
|
|
57
|
+
*
|
|
58
|
+
* Also returns a `stop()` function that callers use to gracefully terminate
|
|
59
|
+
* the monitor after successful execution.
|
|
60
|
+
*/
|
|
61
|
+
export function startLockMonitor(options) {
|
|
62
|
+
const { migrationPid, clientFactory, pollIntervalMs = 500, queueThreshold = 1, signal, } = options;
|
|
63
|
+
let externalStop = false;
|
|
64
|
+
let stopResolve;
|
|
65
|
+
const stopPromise = new Promise(r => { stopResolve = r; });
|
|
66
|
+
/**
|
|
67
|
+
* Abort handler — fires when AbortSignal triggers from the outside
|
|
68
|
+
*/
|
|
69
|
+
function onAbort() {
|
|
70
|
+
stopResolve?.();
|
|
71
|
+
}
|
|
72
|
+
if (signal) {
|
|
73
|
+
if (signal.aborted) {
|
|
74
|
+
onAbort();
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const result = (async () => {
|
|
81
|
+
let client;
|
|
82
|
+
try {
|
|
83
|
+
client = await clientFactory();
|
|
84
|
+
// Set short timeouts on the monitor connection itself
|
|
85
|
+
await client.query(`SET statement_timeout = '5000'`);
|
|
86
|
+
await client.query(`SET lock_timeout = '2000'`);
|
|
87
|
+
while (true) {
|
|
88
|
+
// Yield to event loop & honour stop signals
|
|
89
|
+
await Promise.race([
|
|
90
|
+
new Promise(r => setTimeout(r, pollIntervalMs)),
|
|
91
|
+
stopPromise,
|
|
92
|
+
]);
|
|
93
|
+
// Check if we were asked to stop externally or via signal
|
|
94
|
+
if (externalStop || signal?.aborted) {
|
|
95
|
+
signal?.removeEventListener('abort', onAbort);
|
|
96
|
+
return {
|
|
97
|
+
avalanche: false,
|
|
98
|
+
blockedCount: 0,
|
|
99
|
+
cancelledAt: '',
|
|
100
|
+
stopReason: externalStop ? 'external-stop' : 'aborted',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// Query queue depth
|
|
104
|
+
let blockedCount = 0;
|
|
105
|
+
try {
|
|
106
|
+
const countResult = await client.query(BLOCKED_QUERY_SQL, [migrationPid]);
|
|
107
|
+
blockedCount = parseInt(String(countResult.rows[0]?.['blocked_count'] ?? '0'), 10);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Monitor query failed — skip this cycle rather than crashing
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (blockedCount >= queueThreshold) {
|
|
114
|
+
// Gather rich diagnostics before cancellation
|
|
115
|
+
let blockedBackends = [];
|
|
116
|
+
try {
|
|
117
|
+
const actResult = await client.query(BLOCKED_QUERY_WITH_ACTIVITY_SQL, [migrationPid]);
|
|
118
|
+
blockedBackends = actResult.rows.map(row => ({
|
|
119
|
+
waiterPid: Number(row['waiter_pid']),
|
|
120
|
+
waiterQuery: String(row['waiter_query'] ?? ''),
|
|
121
|
+
waiterState: String(row['waiter_state'] ?? ''),
|
|
122
|
+
waitEventType: String(row['wait_event_type'] ?? ''),
|
|
123
|
+
waitEvent: String(row['wait_event'] ?? ''),
|
|
124
|
+
blockerMode: String(row['blocker_mode'] ?? ''),
|
|
125
|
+
}));
|
|
126
|
+
void blockedBackends; // captured for future extension / structured logging
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// diagnostics are best-effort; proceed to cancel regardless
|
|
130
|
+
}
|
|
131
|
+
// Issue pg_cancel_backend — preferred over pg_terminate_backend
|
|
132
|
+
// because it gives the migration client a chance to clean up.
|
|
133
|
+
try {
|
|
134
|
+
await client.query('SELECT pg_cancel_backend($1)', [migrationPid]);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// If cancellation itself fails (e.g. PID already gone), swallow the error
|
|
138
|
+
}
|
|
139
|
+
const cancelledAt = new Date().toISOString();
|
|
140
|
+
signal?.removeEventListener('abort', onAbort);
|
|
141
|
+
return {
|
|
142
|
+
avalanche: true,
|
|
143
|
+
blockedCount,
|
|
144
|
+
cancelledAt,
|
|
145
|
+
stopReason: 'avalanche',
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
signal?.removeEventListener('abort', onAbort);
|
|
152
|
+
return {
|
|
153
|
+
avalanche: false,
|
|
154
|
+
blockedCount: 0,
|
|
155
|
+
cancelledAt: '',
|
|
156
|
+
stopReason: 'error',
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
try {
|
|
161
|
+
await client?.end();
|
|
162
|
+
}
|
|
163
|
+
catch { /* ignore */ }
|
|
164
|
+
}
|
|
165
|
+
})();
|
|
166
|
+
return {
|
|
167
|
+
result,
|
|
168
|
+
stop: () => {
|
|
169
|
+
externalStop = true;
|
|
170
|
+
stopResolve?.();
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Determines whether a Postgres error code represents a lock-related failure.
|
|
176
|
+
*
|
|
177
|
+
* 55P03 — lock_not_available (SET LOCAL lock_timeout exceeded)
|
|
178
|
+
* 57014 — query_canceled (SET LOCAL statement_timeout exceeded, or pg_cancel_backend)
|
|
179
|
+
*/
|
|
180
|
+
export function isLockError(code) {
|
|
181
|
+
return code === '55P03' || code === '57014';
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=locksMonitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locksMonitor.js","sourceRoot":"","sources":["../../../src/runner/locksMonitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA8CH,kFAAkF;AAClF,MAAM,iBAAiB,GAAG;;;;;;;;;;CAUzB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,+BAA+B,GAAG;;;;;;;;;;;;;;;;;;;CAmBvC,CAAC;AAWF;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAuB;IAItD,MAAM,EACJ,YAAY,EACZ,aAAa,EACb,cAAc,GAAI,GAAG,EACrB,cAAc,GAAI,CAAC,EACnB,MAAM,GACP,GAAG,OAAO,CAAC;IAEZ,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,IAAI,WAAqC,CAAC;IAE1C,MAAM,WAAW,GAAG,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjE;;OAEG;IACH,SAAS,OAAO;QACd,WAAW,EAAE,EAAE,CAAC;IAClB,CAAC;IACD,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO,EAAE,CAAC;QACZ,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,KAAK,IAA4B,EAAE;QACjD,IAAI,MAAiC,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,aAAa,EAAE,CAAC;YAE/B,sDAAsD;YACtD,MAAM,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;YACrD,MAAM,MAAM,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;YAEhD,OAAO,IAAI,EAAE,CAAC;gBACZ,4CAA4C;gBAC5C,MAAM,OAAO,CAAC,IAAI,CAAC;oBACjB,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;oBACrD,WAAW;iBACZ,CAAC,CAAC;gBAEH,0DAA0D;gBAC1D,IAAI,YAAY,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;oBACpC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;oBAC9C,OAAO;wBACL,SAAS,EAAE,KAAK;wBAChB,YAAY,EAAE,CAAC;wBACf,WAAW,EAAE,EAAE;wBACf,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS;qBACvD,CAAC;gBACJ,CAAC;gBAED,oBAAoB;gBACpB,IAAI,YAAY,GAAG,CAAC,CAAC;gBACrB,IAAI,CAAC;oBACH,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;oBAC1E,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;gBACrF,CAAC;gBAAC,MAAM,CAAC;oBACP,8DAA8D;oBAC9D,SAAS;gBACX,CAAC;gBAED,IAAI,YAAY,IAAI,cAAc,EAAE,CAAC;oBACnC,8CAA8C;oBAC9C,IAAI,eAAe,GAAqB,EAAE,CAAC;oBAC3C,IAAI,CAAC;wBACH,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;wBACtF,eAAe,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;4BAC3C,SAAS,EAAO,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;4BACzC,WAAW,EAAK,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;4BACjD,WAAW,EAAK,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;4BACjD,aAAa,EAAG,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC;4BACpD,SAAS,EAAO,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;4BAC/C,WAAW,EAAK,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;yBAClD,CAAC,CAAC,CAAC;wBACJ,KAAK,eAAe,CAAC,CAAC,qDAAqD;oBAC7E,CAAC;oBAAC,MAAM,CAAC;wBACP,4DAA4D;oBAC9D,CAAC;oBAED,gEAAgE;oBAChE,8DAA8D;oBAC9D,IAAI,CAAC;wBACH,MAAM,MAAM,CAAC,KAAK,CAAC,8BAA8B,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;oBACrE,CAAC;oBAAC,MAAM,CAAC;wBACP,0EAA0E;oBAC5E,CAAC;oBAED,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;oBAC7C,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;oBAE9C,OAAO;wBACL,SAAS,EAAE,IAAI;wBACf,YAAY;wBACZ,WAAW;wBACX,UAAU,EAAE,WAAW;qBACxB,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO;gBACL,SAAS,EAAE,KAAK;gBAChB,YAAY,EAAE,CAAC;gBACf,WAAW,EAAE,EAAE;gBACf,UAAU,EAAE,OAAO;aACpB,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC;gBAAC,MAAM,MAAM,EAAE,GAAG,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QACrD,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO;QACL,MAAM;QACN,IAAI,EAAE,GAAG,EAAE;YACT,YAAY,GAAG,IAAI,CAAC;YACpB,WAAW,EAAE,EAAE,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,CAAC;AAC9C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ddlforge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Ultra-fast, zero-runtime-dependency Postgres migration lock linter and data-loss prevention engine CLI in Node.js 20+ and TypeScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "tsc && node -e \"const fs=require('fs'); try{fs.chmodSync('dist/bin/ddlforge.js', 0o755)}catch{}\"",
|
|
18
18
|
"test:build": "tsc -p tsconfig.test.json",
|
|
19
|
-
"test": "npm run test:build && node --test dist/test/analyzer.test.js",
|
|
20
|
-
"test:watch": "node --test --watch dist/test/analyzer.test.js",
|
|
19
|
+
"test": "npm run test:build && node --test dist/test/analyzer.test.js dist/test/runner.test.js",
|
|
20
|
+
"test:watch": "node --test --watch dist/test/analyzer.test.js dist/test/runner.test.js",
|
|
21
21
|
"prepack": "node -e \"const fs=require('fs'); try{fs.rmSync('dist/test', {recursive:true, force:true})}catch{}\"",
|
|
22
22
|
"prepublishOnly": "npm run build && node -e \"const fs=require('fs'); try{fs.rmSync('dist/test', {recursive:true, force:true})}catch{}\""
|
|
23
23
|
},
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^22.13.10",
|
|
42
|
+
"@types/pg": "^8.23.1",
|
|
42
43
|
"typescript": "^5.8.2"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"pg": "^8.23.0"
|
|
43
47
|
}
|
|
44
48
|
}
|