@ftschopp/dynatable-migrations 1.2.4 → 1.2.5

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 CHANGED
@@ -1,3 +1,10 @@
1
+ ## @ftschopp/dynatable-migrations [1.2.5](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-migrations@1.2.4...@ftschopp/dynatable-migrations@1.2.5) (2026-05-08)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **migrations:** make markAsApplied idempotent and surface typed errors ([#29](https://github.com/ftschopp/dynatable/issues/29)) ([133b836](https://github.com/ftschopp/dynatable/commit/133b83656c0891a04a155c603f53220dadaa6f87)), closes [#10](https://github.com/ftschopp/dynatable/issues/10)
7
+
1
8
  ## @ftschopp/dynatable-migrations [1.2.4](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-migrations@1.2.3...@ftschopp/dynatable-migrations@1.2.4) (2026-05-08)
2
9
 
3
10
 
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Thrown by `markAsApplied` when the version already has a tracking record
3
+ * in a non-applied state (e.g. `failed`, `rolled_back`) and therefore
4
+ * cannot be silently treated as an idempotent re-apply. Surfaces the
5
+ * existing state so the caller can decide whether to retry, recover, or
6
+ * roll back.
7
+ */
8
+ export declare class MigrationAlreadyAppliedError extends Error {
9
+ readonly version: string;
10
+ readonly currentStatus: string | undefined;
11
+ constructor(version: string, currentStatus: string | undefined);
12
+ }
13
+ /**
14
+ * Thrown when a tracker write is cancelled because the migration lock was
15
+ * taken by another process between `acquireLock()` and the write itself
16
+ * (e.g. the original lock TTL expired and a second worker took over).
17
+ *
18
+ * The transaction was rolled back atomically — no partial state was
19
+ * written. The safe action is to stop the current `up()` / `down()` run.
20
+ */
21
+ export declare class MigrationLockLostError extends Error {
22
+ readonly version: string;
23
+ constructor(version: string);
24
+ }
25
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,SAAgB,OAAO,EAAE,MAAM,CAAC;IAChC,SAAgB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEtC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,SAAS;CAgB/D;AAED;;;;;;;GAOG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,SAAgB,OAAO,EAAE,MAAM,CAAC;gBAEpB,OAAO,EAAE,MAAM;CAc5B"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MigrationLockLostError = exports.MigrationAlreadyAppliedError = void 0;
4
+ /**
5
+ * Thrown by `markAsApplied` when the version already has a tracking record
6
+ * in a non-applied state (e.g. `failed`, `rolled_back`) and therefore
7
+ * cannot be silently treated as an idempotent re-apply. Surfaces the
8
+ * existing state so the caller can decide whether to retry, recover, or
9
+ * roll back.
10
+ */
11
+ class MigrationAlreadyAppliedError extends Error {
12
+ constructor(version, currentStatus) {
13
+ super(`Migration "${version}" already has a tracking record in state ` +
14
+ `"${currentStatus ?? 'unknown'}" — cannot mark as applied. ` +
15
+ `If you want to re-apply this version, roll it back first ` +
16
+ `(or recover the failed run) and try again.`);
17
+ this.name = 'MigrationAlreadyAppliedError';
18
+ this.version = version;
19
+ this.currentStatus = currentStatus;
20
+ if (typeof Error.captureStackTrace === 'function') {
21
+ Error.captureStackTrace(this, MigrationAlreadyAppliedError);
22
+ }
23
+ }
24
+ }
25
+ exports.MigrationAlreadyAppliedError = MigrationAlreadyAppliedError;
26
+ /**
27
+ * Thrown when a tracker write is cancelled because the migration lock was
28
+ * taken by another process between `acquireLock()` and the write itself
29
+ * (e.g. the original lock TTL expired and a second worker took over).
30
+ *
31
+ * The transaction was rolled back atomically — no partial state was
32
+ * written. The safe action is to stop the current `up()` / `down()` run.
33
+ */
34
+ class MigrationLockLostError extends Error {
35
+ constructor(version) {
36
+ super(`Migration lock was lost during markAsApplied("${version}"). ` +
37
+ `Another worker may have taken over. The transaction was rolled ` +
38
+ `back atomically; no partial state was written.`);
39
+ this.name = 'MigrationLockLostError';
40
+ this.version = version;
41
+ if (typeof Error.captureStackTrace === 'function') {
42
+ Error.captureStackTrace(this, MigrationLockLostError);
43
+ }
44
+ }
45
+ }
46
+ exports.MigrationLockLostError = MigrationLockLostError;
47
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":";;;AAAA;;;;;;GAMG;AACH,MAAa,4BAA6B,SAAQ,KAAK;IAIrD,YAAY,OAAe,EAAE,aAAiC;QAC5D,KAAK,CACH,cAAc,OAAO,2CAA2C;YAC9D,IAAI,aAAa,IAAI,SAAS,8BAA8B;YAC5D,2DAA2D;YAC3D,4CAA4C,CAC/C,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;QAC3C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,OAAQ,KAAoD,CAAC,iBAAiB,KAAK,UAAU,EAAE,CAAC;YAEhG,KACD,CAAC,iBAAiB,CAAC,IAAI,EAAE,4BAA4B,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;CACF;AApBD,oEAoBC;AAED;;;;;;;GAOG;AACH,MAAa,sBAAuB,SAAQ,KAAK;IAG/C,YAAY,OAAe;QACzB,KAAK,CACH,iDAAiD,OAAO,MAAM;YAC5D,iEAAiE;YACjE,gDAAgD,CACnD,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,OAAQ,KAAoD,CAAC,iBAAiB,KAAK,UAAU,EAAE,CAAC;YAEhG,KACD,CAAC,iBAAiB,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;CACF;AAjBD,wDAiBC"}
@@ -42,6 +42,20 @@ export declare class DynamoDBMigrationTracker implements MigrationTracker {
42
42
  * Handles both new migrations and re-applying rolled back migrations
43
43
  */
44
44
  markAsApplied(version: string, name: string, schemaDefinition?: Record<string, any>, schemaChanges?: SchemaChange[], checksum?: string): Promise<void>;
45
+ /**
46
+ * Inspect a `TransactionCanceledException` raised by `markAsApplied` and
47
+ * either swallow it (idempotent re-apply) or re-throw a typed error.
48
+ *
49
+ * The TransactWrite items are: [lockOwnershipCheck, migrationRow, currentPointer].
50
+ * Each item produces an entry in `CancellationReasons`.
51
+ * - reasons[0] failing → the lock was lost → MigrationLockLostError.
52
+ * - reasons[1] failing → the migration row already exists in a state the
53
+ * write refused. Re-fetch the record:
54
+ * * status === 'applied' → idempotent re-apply, return silently.
55
+ * * other states → MigrationAlreadyAppliedError with the
56
+ * current state, so the caller can decide what to do.
57
+ */
58
+ private handleMarkAsAppliedCancellation;
45
59
  /**
46
60
  * Mark migration as rolled back using TransactWrite for atomicity
47
61
  */
@@ -1 +1 @@
1
- {"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/core/tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAS/D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAG5F,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,qBAAa,wBAAyB,YAAW,gBAAgB;IAC/D,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,iDAAiD;IACjD,SAAgB,cAAc,EAAE,MAAM,CAAC;gBAE3B,MAAM,EAAE,sBAAsB,EAAE,MAAM,EAAE,eAAe;IAQnE,OAAO,KAAK,OAAO,GAKlB;IAED,wEAAwE;IACxE,OAAO,CAAC,WAAW;IASnB,gFAAgF;IAChF,OAAO,CAAC,kBAAkB;IAcpB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBjC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAiCrC;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBlC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBlC;;OAEG;IACG,oBAAoB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAuBlD,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAcjD;;;OAGG;IACG,aAAa,CACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACtC,aAAa,CAAC,EAAE,YAAY,EAAE,EAC9B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAuHhB;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuDhD,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8D3D,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCvD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAc9D,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAInD"}
1
+ {"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/core/tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAS/D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAI5F,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,qBAAa,wBAAyB,YAAW,gBAAgB;IAC/D,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,iDAAiD;IACjD,SAAgB,cAAc,EAAE,MAAM,CAAC;gBAE3B,MAAM,EAAE,sBAAsB,EAAE,MAAM,EAAE,eAAe;IAQnE,OAAO,KAAK,OAAO,GAKlB;IAED,wEAAwE;IACxE,OAAO,CAAC,WAAW;IASnB,gFAAgF;IAChF,OAAO,CAAC,kBAAkB;IAcpB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBjC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAiCrC;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBlC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBlC;;OAEG;IACG,oBAAoB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAuBlD,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAcjD;;;OAGG;IACG,aAAa,CACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACtC,aAAa,CAAC,EAAE,YAAY,EAAE,EAC9B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IA2HhB;;;;;;;;;;;;OAYG;YACW,+BAA+B;IA2B7C;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuDhD,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8D3D,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCvD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAc9D,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAInD"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DynamoDBMigrationTracker = exports.DEFAULT_LOCK_TTL_SECONDS = void 0;
4
4
  const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
5
5
  const semver_1 = require("./semver");
6
+ const errors_1 = require("./errors");
6
7
  /** Default lock TTL in seconds. Configurable via MigrationConfig.lockTtlSeconds. */
7
8
  exports.DEFAULT_LOCK_TTL_SECONDS = 300; // 5 minutes
8
9
  class DynamoDBMigrationTracker {
@@ -179,109 +180,149 @@ class DynamoDBMigrationTracker {
179
180
  const now = new Date().toISOString();
180
181
  // Check if migration record already exists (e.g., rolled back or failed)
181
182
  const existing = await this.getMigration(version);
182
- if (existing) {
183
- // Update existing record (re-applying a rolled back or failed migration)
184
- // Build dynamic update expression - DynamoDB doesn't accept undefined values
185
- const setParts = ['#status = :status', 'appliedAt = :appliedAt', '#name = :name'];
186
- const expressionValues = {
187
- ':status': 'applied',
188
- ':appliedAt': now,
189
- ':name': name,
190
- };
191
- if (schemaDefinition !== undefined) {
192
- setParts.push('schemaDefinition = :schemaDef');
193
- expressionValues[':schemaDef'] = schemaDefinition;
194
- }
195
- if (schemaChanges !== undefined) {
196
- setParts.push('schemaChanges = :schemaChanges');
197
- expressionValues[':schemaChanges'] = schemaChanges;
198
- }
199
- if (checksum !== undefined) {
200
- setParts.push('checksum = :checksum');
201
- expressionValues[':checksum'] = checksum;
202
- }
203
- await this.client.send(new lib_dynamodb_1.TransactWriteCommand({
204
- TransactItems: [
205
- this.lockOwnershipCheck(),
206
- {
207
- Update: {
208
- TableName: this.tableName,
209
- Key: {
210
- PK: this.trackingPrefix,
211
- SK: version,
212
- },
213
- UpdateExpression: `SET ${setParts.join(', ')} REMOVE #error, failedAt, rolledBackAt`,
214
- ExpressionAttributeNames: {
215
- '#status': 'status',
216
- '#name': 'name',
217
- '#error': 'error',
183
+ try {
184
+ if (existing) {
185
+ // Update existing record (re-applying a rolled back or failed migration)
186
+ // Build dynamic update expression - DynamoDB doesn't accept undefined values
187
+ const setParts = ['#status = :status', 'appliedAt = :appliedAt', '#name = :name'];
188
+ const expressionValues = {
189
+ ':status': 'applied',
190
+ ':appliedAt': now,
191
+ ':name': name,
192
+ };
193
+ if (schemaDefinition !== undefined) {
194
+ setParts.push('schemaDefinition = :schemaDef');
195
+ expressionValues[':schemaDef'] = schemaDefinition;
196
+ }
197
+ if (schemaChanges !== undefined) {
198
+ setParts.push('schemaChanges = :schemaChanges');
199
+ expressionValues[':schemaChanges'] = schemaChanges;
200
+ }
201
+ if (checksum !== undefined) {
202
+ setParts.push('checksum = :checksum');
203
+ expressionValues[':checksum'] = checksum;
204
+ }
205
+ await this.client.send(new lib_dynamodb_1.TransactWriteCommand({
206
+ TransactItems: [
207
+ this.lockOwnershipCheck(),
208
+ {
209
+ Update: {
210
+ TableName: this.tableName,
211
+ Key: {
212
+ PK: this.trackingPrefix,
213
+ SK: version,
214
+ },
215
+ UpdateExpression: `SET ${setParts.join(', ')} REMOVE #error, failedAt, rolledBackAt`,
216
+ ExpressionAttributeNames: {
217
+ '#status': 'status',
218
+ '#name': 'name',
219
+ '#error': 'error',
220
+ },
221
+ ExpressionAttributeValues: expressionValues,
222
+ // Only allow re-applying if not currently applied
223
+ ConditionExpression: '#status <> :status',
218
224
  },
219
- ExpressionAttributeValues: expressionValues,
220
- // Only allow re-applying if not currently applied
221
- ConditionExpression: '#status <> :status',
222
225
  },
223
- },
224
- {
225
- Update: {
226
- TableName: this.tableName,
227
- Key: {
228
- PK: `${this.trackingPrefix}#CURRENT`,
229
- SK: `${this.trackingPrefix}#CURRENT`,
230
- },
231
- UpdateExpression: 'SET currentVersion = :version, updatedAt = :updatedAt, GSI1SK = :gsi1sk',
232
- ExpressionAttributeValues: {
233
- ':version': version,
234
- ':updatedAt': now,
235
- ':gsi1sk': version,
226
+ {
227
+ Update: {
228
+ TableName: this.tableName,
229
+ Key: {
230
+ PK: `${this.trackingPrefix}#CURRENT`,
231
+ SK: `${this.trackingPrefix}#CURRENT`,
232
+ },
233
+ UpdateExpression: 'SET currentVersion = :version, updatedAt = :updatedAt, GSI1SK = :gsi1sk',
234
+ ExpressionAttributeValues: {
235
+ ':version': version,
236
+ ':updatedAt': now,
237
+ ':gsi1sk': version,
238
+ },
236
239
  },
237
240
  },
238
- },
239
- ],
240
- }));
241
- }
242
- else {
243
- // Create new migration record
244
- await this.client.send(new lib_dynamodb_1.TransactWriteCommand({
245
- TransactItems: [
246
- this.lockOwnershipCheck(),
247
- {
248
- Put: {
249
- TableName: this.tableName,
250
- Item: {
251
- PK: this.trackingPrefix,
252
- SK: version,
253
- version,
254
- name,
255
- timestamp: now,
256
- appliedAt: now,
257
- status: 'applied',
258
- schemaDefinition,
259
- schemaChanges,
260
- checksum,
241
+ ],
242
+ }));
243
+ }
244
+ else {
245
+ // Create new migration record
246
+ await this.client.send(new lib_dynamodb_1.TransactWriteCommand({
247
+ TransactItems: [
248
+ this.lockOwnershipCheck(),
249
+ {
250
+ Put: {
251
+ TableName: this.tableName,
252
+ Item: {
253
+ PK: this.trackingPrefix,
254
+ SK: version,
255
+ version,
256
+ name,
257
+ timestamp: now,
258
+ appliedAt: now,
259
+ status: 'applied',
260
+ schemaDefinition,
261
+ schemaChanges,
262
+ checksum,
263
+ },
264
+ // Ensure migration wasn't already applied (uniqueness on
265
+ // SK; PK is shared across all migration rows).
266
+ ConditionExpression: 'attribute_not_exists(SK)',
261
267
  },
262
- // Ensure migration wasn't already applied (uniqueness on
263
- // SK; PK is shared across all migration rows).
264
- ConditionExpression: 'attribute_not_exists(SK)',
265
268
  },
266
- },
267
- {
268
- Update: {
269
- TableName: this.tableName,
270
- Key: {
271
- PK: `${this.trackingPrefix}#CURRENT`,
272
- SK: `${this.trackingPrefix}#CURRENT`,
273
- },
274
- UpdateExpression: 'SET currentVersion = :version, updatedAt = :updatedAt, GSI1SK = :gsi1sk',
275
- ExpressionAttributeValues: {
276
- ':version': version,
277
- ':updatedAt': now,
278
- ':gsi1sk': version,
269
+ {
270
+ Update: {
271
+ TableName: this.tableName,
272
+ Key: {
273
+ PK: `${this.trackingPrefix}#CURRENT`,
274
+ SK: `${this.trackingPrefix}#CURRENT`,
275
+ },
276
+ UpdateExpression: 'SET currentVersion = :version, updatedAt = :updatedAt, GSI1SK = :gsi1sk',
277
+ ExpressionAttributeValues: {
278
+ ':version': version,
279
+ ':updatedAt': now,
280
+ ':gsi1sk': version,
281
+ },
279
282
  },
280
283
  },
281
- },
282
- ],
283
- }));
284
+ ],
285
+ }));
286
+ }
287
+ }
288
+ catch (err) {
289
+ await this.handleMarkAsAppliedCancellation(err, version);
290
+ }
291
+ }
292
+ /**
293
+ * Inspect a `TransactionCanceledException` raised by `markAsApplied` and
294
+ * either swallow it (idempotent re-apply) or re-throw a typed error.
295
+ *
296
+ * The TransactWrite items are: [lockOwnershipCheck, migrationRow, currentPointer].
297
+ * Each item produces an entry in `CancellationReasons`.
298
+ * - reasons[0] failing → the lock was lost → MigrationLockLostError.
299
+ * - reasons[1] failing → the migration row already exists in a state the
300
+ * write refused. Re-fetch the record:
301
+ * * status === 'applied' → idempotent re-apply, return silently.
302
+ * * other states → MigrationAlreadyAppliedError with the
303
+ * current state, so the caller can decide what to do.
304
+ */
305
+ async handleMarkAsAppliedCancellation(err, version) {
306
+ const error = err;
307
+ if (error?.name !== 'TransactionCanceledException') {
308
+ throw err;
309
+ }
310
+ const reasons = error.CancellationReasons ?? [];
311
+ const lockReason = reasons[0];
312
+ const migrationReason = reasons[1];
313
+ if (lockReason?.Code === 'ConditionalCheckFailed') {
314
+ throw new errors_1.MigrationLockLostError(version);
315
+ }
316
+ if (migrationReason?.Code === 'ConditionalCheckFailed') {
317
+ const current = await this.getMigration(version);
318
+ if (current?.status === 'applied') {
319
+ // Either we re-tried our own write or another worker applied the same
320
+ // version. Either way, the desired post-state is already satisfied.
321
+ return;
322
+ }
323
+ throw new errors_1.MigrationAlreadyAppliedError(version, current?.status);
284
324
  }
325
+ throw err;
285
326
  }
286
327
  /**
287
328
  * Mark migration as rolled back using TransactWrite for atomicity
@@ -1 +1 @@
1
- {"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/core/tracker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/core/tracker.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export { MigrationLoader } from './core/loader';
8
8
  export { MigrationRunner, type RunOptions } from './core/runner';
9
9
  export { ConfigLoader, loadConfig } from './core/config';
10
10
  export { createDynamoDBClient } from './core/client';
11
+ export { MigrationAlreadyAppliedError, MigrationLockLostError } from './core/errors';
11
12
  export { generateMigrationTemplate } from './templates/migration';
12
13
  export { createMigration } from './commands/create';
13
14
  export { runMigrations } from './commands/up';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAGrD,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAGlE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,4BAA4B,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAGrF,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAGlE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
18
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.initProject = exports.showStatus = exports.rollbackMigrations = exports.runMigrations = exports.createMigration = exports.generateMigrationTemplate = exports.createDynamoDBClient = exports.loadConfig = exports.ConfigLoader = exports.MigrationRunner = exports.MigrationLoader = exports.DynamoDBMigrationTracker = void 0;
21
+ exports.initProject = exports.showStatus = exports.rollbackMigrations = exports.runMigrations = exports.createMigration = exports.generateMigrationTemplate = exports.MigrationLockLostError = exports.MigrationAlreadyAppliedError = exports.createDynamoDBClient = exports.loadConfig = exports.ConfigLoader = exports.MigrationRunner = exports.MigrationLoader = exports.DynamoDBMigrationTracker = void 0;
22
22
  // Types
23
23
  __exportStar(require("./types"), exports);
24
24
  // Core
@@ -33,6 +33,9 @@ Object.defineProperty(exports, "ConfigLoader", { enumerable: true, get: function
33
33
  Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_1.loadConfig; } });
34
34
  var client_1 = require("./core/client");
35
35
  Object.defineProperty(exports, "createDynamoDBClient", { enumerable: true, get: function () { return client_1.createDynamoDBClient; } });
36
+ var errors_1 = require("./core/errors");
37
+ Object.defineProperty(exports, "MigrationAlreadyAppliedError", { enumerable: true, get: function () { return errors_1.MigrationAlreadyAppliedError; } });
38
+ Object.defineProperty(exports, "MigrationLockLostError", { enumerable: true, get: function () { return errors_1.MigrationLockLostError; } });
36
39
  // Template
37
40
  var migration_1 = require("./templates/migration");
38
41
  Object.defineProperty(exports, "generateMigrationTemplate", { enumerable: true, get: function () { return migration_1.generateMigrationTemplate; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;AAEH,QAAQ;AACR,0CAAwB;AAExB,OAAO;AACP,0CAA0D;AAAjD,mHAAA,wBAAwB,OAAA;AACjC,wCAAgD;AAAvC,yGAAA,eAAe,OAAA;AACxB,wCAAiE;AAAxD,yGAAA,eAAe,OAAA;AACxB,wCAAyD;AAAhD,sGAAA,YAAY,OAAA;AAAE,oGAAA,UAAU,OAAA;AACjC,wCAAqD;AAA5C,8GAAA,oBAAoB,OAAA;AAE7B,WAAW;AACX,mDAAkE;AAAzD,sHAAA,yBAAyB,OAAA;AAElC,kCAAkC;AAClC,4CAAoD;AAA3C,yGAAA,eAAe,OAAA;AACxB,oCAA8C;AAArC,mGAAA,aAAa,OAAA;AACtB,wCAAqD;AAA5C,0GAAA,kBAAkB,OAAA;AAC3B,4CAA+C;AAAtC,oGAAA,UAAU,OAAA;AACnB,wCAA8C;AAArC,mGAAA,WAAW,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;AAEH,QAAQ;AACR,0CAAwB;AAExB,OAAO;AACP,0CAA0D;AAAjD,mHAAA,wBAAwB,OAAA;AACjC,wCAAgD;AAAvC,yGAAA,eAAe,OAAA;AACxB,wCAAiE;AAAxD,yGAAA,eAAe,OAAA;AACxB,wCAAyD;AAAhD,sGAAA,YAAY,OAAA;AAAE,oGAAA,UAAU,OAAA;AACjC,wCAAqD;AAA5C,8GAAA,oBAAoB,OAAA;AAC7B,wCAAqF;AAA5E,sHAAA,4BAA4B,OAAA;AAAE,gHAAA,sBAAsB,OAAA;AAE7D,WAAW;AACX,mDAAkE;AAAzD,sHAAA,yBAAyB,OAAA;AAElC,kCAAkC;AAClC,4CAAoD;AAA3C,yGAAA,eAAe,OAAA;AACxB,oCAA8C;AAArC,mGAAA,aAAa,OAAA;AACtB,wCAAqD;AAA5C,0GAAA,kBAAkB,OAAA;AAC3B,4CAA+C;AAAtC,oGAAA,UAAU,OAAA;AACnB,wCAA8C;AAArC,mGAAA,WAAW,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ftschopp/dynatable-migrations",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "DynamoDB migration tool for single table design with schema versioning",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Thrown by `markAsApplied` when the version already has a tracking record
3
+ * in a non-applied state (e.g. `failed`, `rolled_back`) and therefore
4
+ * cannot be silently treated as an idempotent re-apply. Surfaces the
5
+ * existing state so the caller can decide whether to retry, recover, or
6
+ * roll back.
7
+ */
8
+ export class MigrationAlreadyAppliedError extends Error {
9
+ public readonly version: string;
10
+ public readonly currentStatus: string | undefined;
11
+
12
+ constructor(version: string, currentStatus: string | undefined) {
13
+ super(
14
+ `Migration "${version}" already has a tracking record in state ` +
15
+ `"${currentStatus ?? 'unknown'}" — cannot mark as applied. ` +
16
+ `If you want to re-apply this version, roll it back first ` +
17
+ `(or recover the failed run) and try again.`
18
+ );
19
+ this.name = 'MigrationAlreadyAppliedError';
20
+ this.version = version;
21
+ this.currentStatus = currentStatus;
22
+ if (typeof (Error as unknown as { captureStackTrace?: unknown }).captureStackTrace === 'function') {
23
+ (
24
+ Error as unknown as { captureStackTrace: (target: object, ctor: unknown) => void }
25
+ ).captureStackTrace(this, MigrationAlreadyAppliedError);
26
+ }
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Thrown when a tracker write is cancelled because the migration lock was
32
+ * taken by another process between `acquireLock()` and the write itself
33
+ * (e.g. the original lock TTL expired and a second worker took over).
34
+ *
35
+ * The transaction was rolled back atomically — no partial state was
36
+ * written. The safe action is to stop the current `up()` / `down()` run.
37
+ */
38
+ export class MigrationLockLostError extends Error {
39
+ public readonly version: string;
40
+
41
+ constructor(version: string) {
42
+ super(
43
+ `Migration lock was lost during markAsApplied("${version}"). ` +
44
+ `Another worker may have taken over. The transaction was rolled ` +
45
+ `back atomically; no partial state was written.`
46
+ );
47
+ this.name = 'MigrationLockLostError';
48
+ this.version = version;
49
+ if (typeof (Error as unknown as { captureStackTrace?: unknown }).captureStackTrace === 'function') {
50
+ (
51
+ Error as unknown as { captureStackTrace: (target: object, ctor: unknown) => void }
52
+ ).captureStackTrace(this, MigrationLockLostError);
53
+ }
54
+ }
55
+ }
@@ -148,3 +148,134 @@ describe('DynamoDBMigrationTracker - tracker writes are gated by lock ownership'
148
148
  expect(put.Put.ConditionExpression).toBe('attribute_not_exists(SK)');
149
149
  });
150
150
  });
151
+
152
+ describe('DynamoDBMigrationTracker - markAsApplied idempotency (#10)', () => {
153
+ function cancelledTransactError(reasons: Array<{ Code?: string }>) {
154
+ return Object.assign(new Error('Transaction cancelled'), {
155
+ name: 'TransactionCanceledException',
156
+ CancellationReasons: reasons,
157
+ });
158
+ }
159
+
160
+ test('returns silently when the cancellation is because the row is already in status=applied', async () => {
161
+ ddbMock.on(PutCommand).resolves({}); // acquireLock
162
+ // First getMigration → no record (so we take the Put branch)
163
+ // Second getMigration (after cancellation) → already applied
164
+ ddbMock
165
+ .on(GetCommand)
166
+ .resolvesOnce({ Item: undefined })
167
+ .resolvesOnce({
168
+ Item: {
169
+ PK: '_SCHEMA#VERSION',
170
+ SK: '0.1.0',
171
+ version: '0.1.0',
172
+ status: 'applied',
173
+ name: 'init',
174
+ },
175
+ });
176
+
177
+ // Migration row condition fails (reasons[1]); lock check passed (reasons[0] = None)
178
+ ddbMock.on(TransactWriteCommand).rejects(
179
+ cancelledTransactError([{ Code: 'None' }, { Code: 'ConditionalCheckFailed' }])
180
+ );
181
+
182
+ const tracker = makeTracker();
183
+ await tracker.acquireLock();
184
+
185
+ await expect(tracker.markAsApplied('0.1.0', 'init')).resolves.toBeUndefined();
186
+ });
187
+
188
+ test('throws MigrationAlreadyAppliedError when the row exists in a non-applied state', async () => {
189
+ ddbMock.on(PutCommand).resolves({});
190
+ ddbMock
191
+ .on(GetCommand)
192
+ .resolvesOnce({ Item: undefined })
193
+ .resolvesOnce({
194
+ Item: {
195
+ PK: '_SCHEMA#VERSION',
196
+ SK: '0.1.0',
197
+ version: '0.1.0',
198
+ status: 'failed',
199
+ name: 'init',
200
+ },
201
+ });
202
+
203
+ ddbMock.on(TransactWriteCommand).rejects(
204
+ cancelledTransactError([{ Code: 'None' }, { Code: 'ConditionalCheckFailed' }])
205
+ );
206
+
207
+ const tracker = makeTracker();
208
+ await tracker.acquireLock();
209
+
210
+ await expect(tracker.markAsApplied('0.1.0', 'init')).rejects.toMatchObject({
211
+ name: 'MigrationAlreadyAppliedError',
212
+ version: '0.1.0',
213
+ currentStatus: 'failed',
214
+ });
215
+ });
216
+
217
+ test('throws MigrationLockLostError when the lock-row ConditionCheck is what failed', async () => {
218
+ ddbMock.on(PutCommand).resolves({});
219
+ ddbMock.on(GetCommand).resolves({ Item: undefined });
220
+
221
+ ddbMock.on(TransactWriteCommand).rejects(
222
+ cancelledTransactError([{ Code: 'ConditionalCheckFailed' }, { Code: 'None' }])
223
+ );
224
+
225
+ const tracker = makeTracker();
226
+ await tracker.acquireLock();
227
+
228
+ await expect(tracker.markAsApplied('0.1.0', 'init')).rejects.toMatchObject({
229
+ name: 'MigrationLockLostError',
230
+ version: '0.1.0',
231
+ });
232
+ });
233
+
234
+ test('re-throws unexpected errors unchanged', async () => {
235
+ ddbMock.on(PutCommand).resolves({});
236
+ ddbMock.on(GetCommand).resolves({ Item: undefined });
237
+
238
+ const boom = Object.assign(new Error('upstream blew up'), { name: 'InternalServerError' });
239
+ ddbMock.on(TransactWriteCommand).rejects(boom);
240
+
241
+ const tracker = makeTracker();
242
+ await tracker.acquireLock();
243
+
244
+ await expect(tracker.markAsApplied('0.1.0', 'init')).rejects.toBe(boom);
245
+ });
246
+
247
+ test('idempotent path also covers the Update branch (existing record was rolled_back, applied by a racing worker)', async () => {
248
+ ddbMock.on(PutCommand).resolves({});
249
+ // First getMigration → existing rolled_back record (so we take the Update branch)
250
+ // Second getMigration (after cancellation) → already applied (race)
251
+ ddbMock
252
+ .on(GetCommand)
253
+ .resolvesOnce({
254
+ Item: {
255
+ PK: '_SCHEMA#VERSION',
256
+ SK: '0.1.0',
257
+ version: '0.1.0',
258
+ status: 'rolled_back',
259
+ name: 'init',
260
+ },
261
+ })
262
+ .resolvesOnce({
263
+ Item: {
264
+ PK: '_SCHEMA#VERSION',
265
+ SK: '0.1.0',
266
+ version: '0.1.0',
267
+ status: 'applied',
268
+ name: 'init',
269
+ },
270
+ });
271
+
272
+ ddbMock.on(TransactWriteCommand).rejects(
273
+ cancelledTransactError([{ Code: 'None' }, { Code: 'ConditionalCheckFailed' }])
274
+ );
275
+
276
+ const tracker = makeTracker();
277
+ await tracker.acquireLock();
278
+
279
+ await expect(tracker.markAsApplied('0.1.0', 'init')).resolves.toBeUndefined();
280
+ });
281
+ });
@@ -9,6 +9,7 @@ import {
9
9
  } from '@aws-sdk/lib-dynamodb';
10
10
  import { MigrationTracker, MigrationRecord, SchemaChange, MigrationConfig } from '../types';
11
11
  import { compareSemver } from './semver';
12
+ import { MigrationAlreadyAppliedError, MigrationLockLostError } from './errors';
12
13
 
13
14
  /** Default lock TTL in seconds. Configurable via MigrationConfig.lockTtlSeconds. */
14
15
  export const DEFAULT_LOCK_TTL_SECONDS = 300; // 5 minutes
@@ -229,116 +230,160 @@ export class DynamoDBMigrationTracker implements MigrationTracker {
229
230
  // Check if migration record already exists (e.g., rolled back or failed)
230
231
  const existing = await this.getMigration(version);
231
232
 
232
- if (existing) {
233
- // Update existing record (re-applying a rolled back or failed migration)
234
- // Build dynamic update expression - DynamoDB doesn't accept undefined values
235
- const setParts = ['#status = :status', 'appliedAt = :appliedAt', '#name = :name'];
236
- const expressionValues: Record<string, any> = {
237
- ':status': 'applied',
238
- ':appliedAt': now,
239
- ':name': name,
240
- };
241
-
242
- if (schemaDefinition !== undefined) {
243
- setParts.push('schemaDefinition = :schemaDef');
244
- expressionValues[':schemaDef'] = schemaDefinition;
245
- }
246
- if (schemaChanges !== undefined) {
247
- setParts.push('schemaChanges = :schemaChanges');
248
- expressionValues[':schemaChanges'] = schemaChanges;
249
- }
250
- if (checksum !== undefined) {
251
- setParts.push('checksum = :checksum');
252
- expressionValues[':checksum'] = checksum;
253
- }
254
-
255
- await this.client.send(
256
- new TransactWriteCommand({
257
- TransactItems: [
258
- this.lockOwnershipCheck(),
259
- {
260
- Update: {
261
- TableName: this.tableName,
262
- Key: {
263
- PK: this.trackingPrefix,
264
- SK: version,
265
- },
266
- UpdateExpression: `SET ${setParts.join(', ')} REMOVE #error, failedAt, rolledBackAt`,
267
- ExpressionAttributeNames: {
268
- '#status': 'status',
269
- '#name': 'name',
270
- '#error': 'error',
233
+ try {
234
+ if (existing) {
235
+ // Update existing record (re-applying a rolled back or failed migration)
236
+ // Build dynamic update expression - DynamoDB doesn't accept undefined values
237
+ const setParts = ['#status = :status', 'appliedAt = :appliedAt', '#name = :name'];
238
+ const expressionValues: Record<string, any> = {
239
+ ':status': 'applied',
240
+ ':appliedAt': now,
241
+ ':name': name,
242
+ };
243
+
244
+ if (schemaDefinition !== undefined) {
245
+ setParts.push('schemaDefinition = :schemaDef');
246
+ expressionValues[':schemaDef'] = schemaDefinition;
247
+ }
248
+ if (schemaChanges !== undefined) {
249
+ setParts.push('schemaChanges = :schemaChanges');
250
+ expressionValues[':schemaChanges'] = schemaChanges;
251
+ }
252
+ if (checksum !== undefined) {
253
+ setParts.push('checksum = :checksum');
254
+ expressionValues[':checksum'] = checksum;
255
+ }
256
+
257
+ await this.client.send(
258
+ new TransactWriteCommand({
259
+ TransactItems: [
260
+ this.lockOwnershipCheck(),
261
+ {
262
+ Update: {
263
+ TableName: this.tableName,
264
+ Key: {
265
+ PK: this.trackingPrefix,
266
+ SK: version,
267
+ },
268
+ UpdateExpression: `SET ${setParts.join(', ')} REMOVE #error, failedAt, rolledBackAt`,
269
+ ExpressionAttributeNames: {
270
+ '#status': 'status',
271
+ '#name': 'name',
272
+ '#error': 'error',
273
+ },
274
+ ExpressionAttributeValues: expressionValues,
275
+ // Only allow re-applying if not currently applied
276
+ ConditionExpression: '#status <> :status',
271
277
  },
272
- ExpressionAttributeValues: expressionValues,
273
- // Only allow re-applying if not currently applied
274
- ConditionExpression: '#status <> :status',
275
278
  },
276
- },
277
- {
278
- Update: {
279
- TableName: this.tableName,
280
- Key: {
281
- PK: `${this.trackingPrefix}#CURRENT`,
282
- SK: `${this.trackingPrefix}#CURRENT`,
283
- },
284
- UpdateExpression:
285
- 'SET currentVersion = :version, updatedAt = :updatedAt, GSI1SK = :gsi1sk',
286
- ExpressionAttributeValues: {
287
- ':version': version,
288
- ':updatedAt': now,
289
- ':gsi1sk': version,
279
+ {
280
+ Update: {
281
+ TableName: this.tableName,
282
+ Key: {
283
+ PK: `${this.trackingPrefix}#CURRENT`,
284
+ SK: `${this.trackingPrefix}#CURRENT`,
285
+ },
286
+ UpdateExpression:
287
+ 'SET currentVersion = :version, updatedAt = :updatedAt, GSI1SK = :gsi1sk',
288
+ ExpressionAttributeValues: {
289
+ ':version': version,
290
+ ':updatedAt': now,
291
+ ':gsi1sk': version,
292
+ },
290
293
  },
291
294
  },
292
- },
293
- ],
294
- })
295
- );
296
- } else {
297
- // Create new migration record
298
- await this.client.send(
299
- new TransactWriteCommand({
300
- TransactItems: [
301
- this.lockOwnershipCheck(),
302
- {
303
- Put: {
304
- TableName: this.tableName,
305
- Item: {
306
- PK: this.trackingPrefix,
307
- SK: version,
308
- version,
309
- name,
310
- timestamp: now,
311
- appliedAt: now,
312
- status: 'applied',
313
- schemaDefinition,
314
- schemaChanges,
315
- checksum,
316
- } as MigrationRecord,
317
- // Ensure migration wasn't already applied (uniqueness on
318
- // SK; PK is shared across all migration rows).
319
- ConditionExpression: 'attribute_not_exists(SK)',
320
- },
321
- },
322
- {
323
- Update: {
324
- TableName: this.tableName,
325
- Key: {
326
- PK: `${this.trackingPrefix}#CURRENT`,
327
- SK: `${this.trackingPrefix}#CURRENT`,
295
+ ],
296
+ })
297
+ );
298
+ } else {
299
+ // Create new migration record
300
+ await this.client.send(
301
+ new TransactWriteCommand({
302
+ TransactItems: [
303
+ this.lockOwnershipCheck(),
304
+ {
305
+ Put: {
306
+ TableName: this.tableName,
307
+ Item: {
308
+ PK: this.trackingPrefix,
309
+ SK: version,
310
+ version,
311
+ name,
312
+ timestamp: now,
313
+ appliedAt: now,
314
+ status: 'applied',
315
+ schemaDefinition,
316
+ schemaChanges,
317
+ checksum,
318
+ } as MigrationRecord,
319
+ // Ensure migration wasn't already applied (uniqueness on
320
+ // SK; PK is shared across all migration rows).
321
+ ConditionExpression: 'attribute_not_exists(SK)',
328
322
  },
329
- UpdateExpression:
330
- 'SET currentVersion = :version, updatedAt = :updatedAt, GSI1SK = :gsi1sk',
331
- ExpressionAttributeValues: {
332
- ':version': version,
333
- ':updatedAt': now,
334
- ':gsi1sk': version,
323
+ },
324
+ {
325
+ Update: {
326
+ TableName: this.tableName,
327
+ Key: {
328
+ PK: `${this.trackingPrefix}#CURRENT`,
329
+ SK: `${this.trackingPrefix}#CURRENT`,
330
+ },
331
+ UpdateExpression:
332
+ 'SET currentVersion = :version, updatedAt = :updatedAt, GSI1SK = :gsi1sk',
333
+ ExpressionAttributeValues: {
334
+ ':version': version,
335
+ ':updatedAt': now,
336
+ ':gsi1sk': version,
337
+ },
335
338
  },
336
339
  },
337
- },
338
- ],
339
- })
340
- );
340
+ ],
341
+ })
342
+ );
343
+ }
344
+ } catch (err: unknown) {
345
+ await this.handleMarkAsAppliedCancellation(err, version);
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Inspect a `TransactionCanceledException` raised by `markAsApplied` and
351
+ * either swallow it (idempotent re-apply) or re-throw a typed error.
352
+ *
353
+ * The TransactWrite items are: [lockOwnershipCheck, migrationRow, currentPointer].
354
+ * Each item produces an entry in `CancellationReasons`.
355
+ * - reasons[0] failing → the lock was lost → MigrationLockLostError.
356
+ * - reasons[1] failing → the migration row already exists in a state the
357
+ * write refused. Re-fetch the record:
358
+ * * status === 'applied' → idempotent re-apply, return silently.
359
+ * * other states → MigrationAlreadyAppliedError with the
360
+ * current state, so the caller can decide what to do.
361
+ */
362
+ private async handleMarkAsAppliedCancellation(
363
+ err: unknown,
364
+ version: string
365
+ ): Promise<void> {
366
+ const error = err as { name?: string; CancellationReasons?: Array<{ Code?: string }> } | null;
367
+ if (error?.name !== 'TransactionCanceledException') {
368
+ throw err;
369
+ }
370
+ const reasons = error.CancellationReasons ?? [];
371
+ const lockReason = reasons[0];
372
+ const migrationReason = reasons[1];
373
+
374
+ if (lockReason?.Code === 'ConditionalCheckFailed') {
375
+ throw new MigrationLockLostError(version);
376
+ }
377
+ if (migrationReason?.Code === 'ConditionalCheckFailed') {
378
+ const current = await this.getMigration(version);
379
+ if (current?.status === 'applied') {
380
+ // Either we re-tried our own write or another worker applied the same
381
+ // version. Either way, the desired post-state is already satisfied.
382
+ return;
383
+ }
384
+ throw new MigrationAlreadyAppliedError(version, current?.status);
341
385
  }
386
+ throw err;
342
387
  }
343
388
 
344
389
  /**
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ export { MigrationLoader } from './core/loader';
12
12
  export { MigrationRunner, type RunOptions } from './core/runner';
13
13
  export { ConfigLoader, loadConfig } from './core/config';
14
14
  export { createDynamoDBClient } from './core/client';
15
+ export { MigrationAlreadyAppliedError, MigrationLockLostError } from './core/errors';
15
16
 
16
17
  // Template
17
18
  export { generateMigrationTemplate } from './templates/migration';