@zudojs/transactions 1.0.0 → 1.1.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 CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  Transaction lifecycle and coordination with state machine, AsyncLocalStorage context propagation, savepoints, hooks, and adapter abstraction.
4
4
 
5
+ <!-- zudo-docs:start -->
6
+
7
+ **Documentation:** [zudojs.oyinlola.site/docs/packages-transactions](https://zudojs.oyinlola.site/docs/packages-transactions) · **For AI agents:** [Markdown version](https://zudojs.oyinlola.site/docs/packages-transactions.md), [llms.txt](https://zudojs.oyinlola.site/llms.txt)
8
+
9
+ <!-- zudo-docs:end -->
10
+
5
11
  ## Installation
6
12
 
7
13
  ```bash
@@ -103,6 +109,21 @@ the transaction.
103
109
  - Committing a transaction that is not active throws rather than silently
104
110
  doing nothing. Only an already-committed transaction is a no-op.
105
111
  - A `nested` transaction rolls back to its savepoint, never to the connection.
112
+ Savepoints are always created on the connection, including when the
113
+ enclosing scope is itself a savepoint or a participant.
114
+ - Releasing a savepoint is not a commit. `afterCommit` and `afterRollback`
115
+ callbacks registered inside a `nested` block, and `hooks.afterCommit` for the
116
+ savepoint, move to the enclosing transaction on release. They run only when
117
+ the outermost transaction commits (or, for `afterRollback`, rolls back). A
118
+ savepoint that is itself rolled back runs its `afterRollback` callbacks at
119
+ once and discards its `afterCommit` callbacks.
120
+ - `begin()` and `run()` both honour `timeout`; completing a transaction through
121
+ `manager.commit()` / `manager.rollback()` releases its timer and registry entry.
122
+ - Failures thrown by `afterCommit` callbacks never undo the commit; they are
123
+ reported to `hooks.onError` as an `AggregateError`.
124
+ - `retry` replays only attempts that opened their own transaction. An attempt
125
+ that joined an enclosing transaction has marked it rollback-only and is not
126
+ replayed.
106
127
 
107
128
  ## Use Cases
108
129
 
@@ -98,6 +98,13 @@ export async function commitTransaction(transaction, adapter, hooks, emit = noop
98
98
  if (hooks?.beforeCommit)
99
99
  await hooks.beforeCommit({ transaction });
100
100
  emit(TRANSACTION_EVENTS.COMMITTING, transaction);
101
+ if (hooks?.afterCommit && transaction.kind === "savepoint") {
102
+ // Releasing a savepoint is not a commit. Registered as a callback, the
103
+ // hook is deferred with the savepoint's own callbacks and fires only if
104
+ // the outermost transaction commits.
105
+ const afterCommit = hooks.afterCommit;
106
+ transaction.afterCommit(() => afterCommit({ transaction }));
107
+ }
101
108
  try {
102
109
  await adapterCommit(transaction, adapter);
103
110
  await transaction.commit();
@@ -110,8 +117,18 @@ export async function commitTransaction(transaction, adapter, hooks, emit = noop
110
117
  throw new TransactionCommitError(transaction.id, error);
111
118
  }
112
119
  emit(TRANSACTION_EVENTS.COMMITTED, transaction);
113
- if (hooks?.afterCommit)
120
+ // afterCommit callbacks that threw did not undo the commit, but they
121
+ // used to fail silently. Report them without changing the outcome.
122
+ const callbackErrors = internals(transaction)._drainCallbackErrors();
123
+ if (callbackErrors.length > 0 && hooks?.onError) {
124
+ await hooks.onError({
125
+ transaction,
126
+ error: new AggregateError(callbackErrors, "after-commit callback failures"),
127
+ });
128
+ }
129
+ if (hooks?.afterCommit && transaction.kind !== "savepoint") {
114
130
  await hooks.afterCommit({ transaction });
131
+ }
115
132
  }
116
133
  /**
117
134
  * Rollback a transaction with hooks and adapter coordination.
@@ -41,7 +41,13 @@ export declare function createTransactionManager(options: TransactionManagerOpti
41
41
  * participant marks the enclosing transaction rollback-only instead.
42
42
  */
43
43
  run<T>(callback: (transaction: Transaction) => Promise<T>, opts?: TransactionOptions): Promise<T>;
44
- /** Commit a transaction. Participants and committed transactions are no-ops. */
44
+ /**
45
+ * Commit a transaction. Participants and committed transactions are no-ops.
46
+ *
47
+ * Completing a transaction opened with `begin()` also releases its
48
+ * timeout timer and registry entry; both used to be released only by
49
+ * `run()`, so hand-managed transactions stayed in the registry forever.
50
+ */
45
51
  commit(transaction: Transaction): Promise<void>;
46
52
  /** Roll back a transaction, or mark the joined transaction rollback-only. */
47
53
  rollback(transaction: Transaction, reason?: unknown): Promise<void>;
@@ -5,11 +5,16 @@
5
5
  */
6
6
  import { getDefaultContext } from "../context/context.core.js";
7
7
  import { internals } from "../transaction/transaction.internal.js";
8
+ import { isTerminal } from "../transaction/transactionStateMachine.js";
8
9
  import { TransactionRollbackError } from "../transactionErrors/transactionError.types.js";
9
10
  import { commitTransaction, rollbackTransaction } from "./manager.commit.js";
10
11
  import { resolvePropagation, suspendsTransaction, } from "./manager.propagation.js";
11
12
  import { withRetry } from "./manager.retry.js";
12
13
  import { createEmitter, TRANSACTION_EVENTS } from "./manager.events.js";
14
+ /** Whether a transaction can no longer change state. */
15
+ function isFinished(transaction) {
16
+ return isTerminal(transaction.state);
17
+ }
13
18
  /**
14
19
  * Create a transaction manager.
15
20
  */
@@ -17,17 +22,41 @@ export function createTransactionManager(options) {
17
22
  const { adapter, hooks, registry } = options;
18
23
  const context = options.context ?? getDefaultContext();
19
24
  const emit = createEmitter(options.onEvent);
20
- /** Arms the timeout, returning a disposer that always clears the timer. */
25
+ /** Timers armed for owned transactions, cleared when they complete. */
26
+ const timers = new Map();
27
+ /** Whether the manager completes this handle (root or savepoint). */
28
+ function owns(transaction) {
29
+ return transaction.kind === "root" || transaction.kind === "savepoint";
30
+ }
31
+ /**
32
+ * Arms the timeout for an owned transaction.
33
+ *
34
+ * `begin()` used to validate `timeout` against the adapter's capabilities
35
+ * and then ignore it — only `run()` armed a timer — so a transaction
36
+ * opened by hand never timed out.
37
+ */
21
38
  function armTimeout(transaction) {
22
39
  const timeout = transaction.options.timeout;
23
40
  if (!timeout || timeout <= 0)
24
- return () => { };
41
+ return;
25
42
  const timer = setTimeout(() => {
43
+ timers.delete(transaction.id);
26
44
  internals(transaction)._markTimedOut();
27
45
  transaction.markRollbackOnly("timeout");
28
46
  emit(TRANSACTION_EVENTS.TIMED_OUT, transaction);
29
47
  }, timeout);
30
- return () => clearTimeout(timer);
48
+ timers.set(transaction.id, timer);
49
+ }
50
+ /** Clears the timer and registry entry of a completed owned transaction. */
51
+ function release(transaction) {
52
+ if (!owns(transaction))
53
+ return;
54
+ const timer = timers.get(transaction.id);
55
+ if (timer !== undefined) {
56
+ clearTimeout(timer);
57
+ timers.delete(transaction.id);
58
+ }
59
+ registry?.unregister(transaction.id);
31
60
  }
32
61
  return {
33
62
  /**
@@ -49,8 +78,9 @@ export function createTransactionManager(options) {
49
78
  hooks,
50
79
  emit,
51
80
  });
52
- if (transaction.kind === "root" || transaction.kind === "savepoint") {
81
+ if (owns(transaction)) {
53
82
  registry?.register(transaction);
83
+ armTimeout(transaction);
54
84
  }
55
85
  return transaction;
56
86
  },
@@ -62,10 +92,21 @@ export function createTransactionManager(options) {
62
92
  * participant marks the enclosing transaction rollback-only instead.
63
93
  */
64
94
  async run(callback, opts) {
65
- return withRetry(opts?.retry, async () => {
95
+ // A failed attempt that only JOINED an enclosing transaction has not
96
+ // been rolled back — it marked the enclosing transaction rollback-only
97
+ // — so replaying it would repeat its side effects inside a transaction
98
+ // that can no longer commit. Retry only attempts this call owned.
99
+ let joined = false;
100
+ const retry = opts?.retry;
101
+ const retryOptions = retry === undefined
102
+ ? undefined
103
+ : {
104
+ ...retry,
105
+ shouldRetry: (error, attempt) => !joined && (retry.shouldRetry?.(error, attempt) ?? true),
106
+ };
107
+ return withRetry(retryOptions, async () => {
66
108
  const transaction = await this.begin(opts);
67
- const owned = transaction.kind === "root" || transaction.kind === "savepoint";
68
- const disposeTimeout = owned ? armTimeout(transaction) : () => { };
109
+ joined = transaction.kind === "participant";
69
110
  const body = async () => {
70
111
  try {
71
112
  const result = await callback(transaction);
@@ -87,9 +128,7 @@ export function createTransactionManager(options) {
87
128
  throw error;
88
129
  }
89
130
  finally {
90
- disposeTimeout();
91
- if (owned)
92
- registry?.unregister(transaction.id);
131
+ release(transaction);
93
132
  }
94
133
  };
95
134
  return transaction.kind === "none"
@@ -97,13 +136,31 @@ export function createTransactionManager(options) {
97
136
  : context.run(transaction, body);
98
137
  });
99
138
  },
100
- /** Commit a transaction. Participants and committed transactions are no-ops. */
139
+ /**
140
+ * Commit a transaction. Participants and committed transactions are no-ops.
141
+ *
142
+ * Completing a transaction opened with `begin()` also releases its
143
+ * timeout timer and registry entry; both used to be released only by
144
+ * `run()`, so hand-managed transactions stayed in the registry forever.
145
+ */
101
146
  async commit(transaction) {
102
- return commitTransaction(transaction, adapter, hooks, emit);
147
+ try {
148
+ await commitTransaction(transaction, adapter, hooks, emit);
149
+ }
150
+ finally {
151
+ if (isFinished(transaction))
152
+ release(transaction);
153
+ }
103
154
  },
104
155
  /** Roll back a transaction, or mark the joined transaction rollback-only. */
105
156
  async rollback(transaction, reason) {
106
- return rollbackTransaction(transaction, adapter, reason, hooks, emit);
157
+ try {
158
+ await rollbackTransaction(transaction, adapter, reason, hooks, emit);
159
+ }
160
+ finally {
161
+ if (isFinished(transaction))
162
+ release(transaction);
163
+ }
107
164
  },
108
165
  /** The transaction in scope for the current async execution, if any. */
109
166
  getCurrent() {
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { createTransaction } from "../transaction/transaction.core.js";
11
11
  import { createNonTransactional, createParticipant, } from "../transaction/transaction.participant.js";
12
- import { internals } from "../transaction/transaction.internal.js";
12
+ import { connectionHandle, internals, } from "../transaction/transaction.internal.js";
13
13
  import { SavepointError, TransactionCapabilityError, TransactionPropagationError, } from "../transactionErrors/transactionError.types.js";
14
14
  import { assertAdapterSupports } from "./manager.capabilities.js";
15
15
  import { noopEmitter, TRANSACTION_EVENTS } from "./manager.events.js";
@@ -56,11 +56,13 @@ async function beginSavepoint(parent, context) {
56
56
  // rule violation, and TransactionCapabilityError names what is missing.
57
57
  throw new TransactionCapabilityError("savepoints, required by nested transactions");
58
58
  }
59
- const child = createTransaction(opts, parent.id, "savepoint");
59
+ const child = createTransaction(opts, parent.id, "savepoint", parent);
60
60
  if (hooks?.beforeBegin)
61
61
  await hooks.beforeBegin({ transaction: child });
62
62
  const savepoint = `sp_${child.id}`;
63
- const parentHandle = internals(parent)._getHandle();
63
+ // Always the connection: a nested run inside another nested run must
64
+ // create its savepoint on the connection, not on the outer savepoint.
65
+ const parentHandle = connectionHandle(parent);
64
66
  try {
65
67
  await adapter.createSavepoint(parentHandle, savepoint);
66
68
  internals(child)._setHandle({ parent: parentHandle, savepoint });
@@ -9,6 +9,8 @@ import type { TransactionKind } from "../transactionTypes/transactionState.js";
9
9
  * @param options - Options the transaction was started with.
10
10
  * @param parentId - Enclosing transaction id, for nested transactions.
11
11
  * @param kind - How this handle relates to the adapter transaction.
12
+ * @param parent - The enclosing transaction of a savepoint. Its callbacks
13
+ * are deferred to `parent` on release instead of running.
12
14
  */
13
- export declare function createTransaction(options?: TransactionOptions, parentId?: string, kind?: TransactionKind): Transaction;
15
+ export declare function createTransaction(options?: TransactionOptions, parentId?: string, kind?: TransactionKind, parent?: Transaction): Transaction;
14
16
  //# sourceMappingURL=transaction.core.d.ts.map
@@ -5,6 +5,7 @@ import { randomBytes } from "node:crypto";
5
5
  import { TransactionRollbackError, TransactionStateError, } from "../transactionErrors/transactionError.types.js";
6
6
  import { canTransition, createTransitionFunction, } from "./transactionStateMachine.js";
7
7
  import { attachInternals } from "./transaction.internal.js";
8
+ import { deferCallbacksToParent } from "./transaction.savepoint.js";
8
9
  /**
9
10
  * Generate a unique transaction ID.
10
11
  */
@@ -30,14 +31,17 @@ async function runCallbacks(callbacks) {
30
31
  * @param options - Options the transaction was started with.
31
32
  * @param parentId - Enclosing transaction id, for nested transactions.
32
33
  * @param kind - How this handle relates to the adapter transaction.
34
+ * @param parent - The enclosing transaction of a savepoint. Its callbacks
35
+ * are deferred to `parent` on release instead of running.
33
36
  */
34
- export function createTransaction(options = {}, parentId, kind = "root") {
37
+ export function createTransaction(options = {}, parentId, kind = "root", parent) {
35
38
  let state = "pending";
36
39
  let rollbackOnly = false;
37
40
  let rollbackOnlyReason;
38
41
  let timedOut = false;
39
42
  const afterCommitCallbacks = [];
40
43
  const afterRollbackCallbacks = [];
44
+ let callbackErrors = [];
41
45
  let handle;
42
46
  const transition = createTransitionFunction(() => state, (next) => {
43
47
  state = next;
@@ -90,8 +94,14 @@ export function createTransaction(options = {}, parentId, kind = "root") {
90
94
  }
91
95
  transition("committing");
92
96
  transition("committed");
97
+ if (kind === "savepoint" && parent !== undefined) {
98
+ deferCallbacksToParent(parent, afterCommitCallbacks, afterRollbackCallbacks);
99
+ return;
100
+ }
93
101
  afterRollbackCallbacks.length = 0;
94
- await runCallbacks(afterCommitCallbacks);
102
+ // The commit stands whatever the callbacks do; their failures are
103
+ // kept for the manager to report instead of being dropped.
104
+ callbackErrors = await runCallbacks(afterCommitCallbacks);
95
105
  },
96
106
  async rollback(reason) {
97
107
  if (state === "rolled_back" || state === "failed")
@@ -137,6 +147,7 @@ export function createTransaction(options = {}, parentId, kind = "root") {
137
147
  timedOut = true;
138
148
  },
139
149
  _getRollbackOnlyReason: () => rollbackOnlyReason,
150
+ _drainCallbackErrors: () => callbackErrors.splice(0),
140
151
  });
141
152
  }
142
153
  //# sourceMappingURL=transaction.core.js.map
@@ -31,6 +31,14 @@ export interface TransactionInternals {
31
31
  _markTimedOut(): void;
32
32
  /** The reason supplied to `markRollbackOnly`, if any. */
33
33
  _getRollbackOnlyReason(): unknown;
34
+ /**
35
+ * Take the failures collected from `afterCommit` callbacks.
36
+ *
37
+ * The transaction stays committed when a callback throws — nothing can
38
+ * undo the adapter commit — but the failures used to vanish without a
39
+ * trace. The manager drains them and reports them to `hooks.onError`.
40
+ */
41
+ _drainCallbackErrors(): unknown[];
34
42
  }
35
43
  /**
36
44
  * Access the manager-only surface of a transaction.
@@ -55,6 +63,18 @@ export interface SavepointHandle {
55
63
  /** The savepoint name created on that handle. */
56
64
  readonly savepoint: string;
57
65
  }
66
+ /**
67
+ * Resolve the adapter connection a transaction runs on.
68
+ *
69
+ * A savepoint's handle names its parent connection; a participant's handle
70
+ * is the joined transaction's. A savepoint opened inside another savepoint
71
+ * used to be created against the outer *savepoint handle* rather than the
72
+ * connection, which no adapter can act on.
73
+ *
74
+ * @param transaction - Any transaction created by this package.
75
+ * @returns The connection-level adapter handle.
76
+ */
77
+ export declare function connectionHandle(transaction: Transaction): unknown;
58
78
  /**
59
79
  * Narrow an adapter handle to a savepoint handle.
60
80
  *
@@ -48,6 +48,22 @@ export function attachInternals(transaction, operations) {
48
48
  });
49
49
  return transaction;
50
50
  }
51
+ /**
52
+ * Resolve the adapter connection a transaction runs on.
53
+ *
54
+ * A savepoint's handle names its parent connection; a participant's handle
55
+ * is the joined transaction's. A savepoint opened inside another savepoint
56
+ * used to be created against the outer *savepoint handle* rather than the
57
+ * connection, which no adapter can act on.
58
+ *
59
+ * @param transaction - Any transaction created by this package.
60
+ * @returns The connection-level adapter handle.
61
+ */
62
+ export function connectionHandle(transaction) {
63
+ const handle = internals(transaction)._getHandle();
64
+ const savepoint = asSavepointHandle(handle);
65
+ return savepoint ? savepoint.parent : handle;
66
+ }
51
67
  /**
52
68
  * Narrow an adapter handle to a savepoint handle.
53
69
  *
@@ -10,7 +10,7 @@
10
10
  * @module transaction/transaction.participant
11
11
  */
12
12
  import { createTransaction } from "./transaction.core.js";
13
- import { internals } from "./transaction.internal.js";
13
+ import { attachInternals, internals } from "./transaction.internal.js";
14
14
  /**
15
15
  * Create a handle that joins an in-progress transaction.
16
16
  *
@@ -18,7 +18,7 @@ import { internals } from "./transaction.internal.js";
18
18
  * @returns A participant handle delegating to `parent`.
19
19
  */
20
20
  export function createParticipant(parent) {
21
- return Object.freeze({
21
+ const participant = {
22
22
  get id() {
23
23
  return parent.id;
24
24
  },
@@ -61,7 +61,25 @@ export function createParticipant(parent) {
61
61
  afterRollback(callback) {
62
62
  parent.afterRollback(callback);
63
63
  },
64
+ };
65
+ // A participant owns nothing, but the manager still has to reach the
66
+ // adapter handle of the transaction it joined: a `nested` run inside a
67
+ // participant scope used to throw "Transaction was not created by
68
+ // @zudojs/transactions" because the frozen participant carried no
69
+ // internals at all. Reads delegate to the joined transaction; writes
70
+ // are refused, since only the owner may drive its state.
71
+ const refuse = () => {
72
+ throw new TypeError("A participant does not own the transaction it joined and cannot modify it.");
73
+ };
74
+ attachInternals(participant, {
75
+ _setHandle: refuse,
76
+ _getHandle: () => internals(parent)._getHandle(),
77
+ _transition: refuse,
78
+ _markTimedOut: refuse,
79
+ _getRollbackOnlyReason: () => internals(parent)._getRollbackOnlyReason(),
80
+ _drainCallbackErrors: () => [],
64
81
  });
82
+ return Object.freeze(participant);
65
83
  }
66
84
  /**
67
85
  * Create a handle for a deliberately non-transactional scope.
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Savepoint callback deferral.
3
+ *
4
+ * Releasing a savepoint is not a commit: the enclosing transaction can still
5
+ * roll back and discard everything the savepoint did. Callbacks registered on
6
+ * a savepoint therefore move to its parent when the savepoint is released,
7
+ * the same way a participant registers straight on the transaction it
8
+ * joined. They run once the outermost transaction settles: `afterCommit`
9
+ * when it commits, `afterRollback` when it rolls back.
10
+ *
11
+ * @module transaction/transaction.savepoint
12
+ */
13
+ import type { Transaction } from "../transactionTypes/transaction.interface.js";
14
+ /**
15
+ * Move a released savepoint's pending callbacks onto its parent.
16
+ *
17
+ * Both arrays are emptied: the callbacks now belong to the parent.
18
+ *
19
+ * @param parent - The transaction the savepoint was created on.
20
+ * @param afterCommit - The savepoint's pending after-commit callbacks.
21
+ * @param afterRollback - The savepoint's pending after-rollback callbacks.
22
+ */
23
+ export declare function deferCallbacksToParent(parent: Transaction, afterCommit: Array<() => Promise<void>>, afterRollback: Array<() => Promise<void>>): void;
24
+ //# sourceMappingURL=transaction.savepoint.d.ts.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Savepoint callback deferral.
3
+ *
4
+ * Releasing a savepoint is not a commit: the enclosing transaction can still
5
+ * roll back and discard everything the savepoint did. Callbacks registered on
6
+ * a savepoint therefore move to its parent when the savepoint is released,
7
+ * the same way a participant registers straight on the transaction it
8
+ * joined. They run once the outermost transaction settles: `afterCommit`
9
+ * when it commits, `afterRollback` when it rolls back.
10
+ *
11
+ * @module transaction/transaction.savepoint
12
+ */
13
+ /**
14
+ * Move a released savepoint's pending callbacks onto its parent.
15
+ *
16
+ * Both arrays are emptied: the callbacks now belong to the parent.
17
+ *
18
+ * @param parent - The transaction the savepoint was created on.
19
+ * @param afterCommit - The savepoint's pending after-commit callbacks.
20
+ * @param afterRollback - The savepoint's pending after-rollback callbacks.
21
+ */
22
+ export function deferCallbacksToParent(parent, afterCommit, afterRollback) {
23
+ for (const callback of afterCommit.splice(0))
24
+ parent.afterCommit(callback);
25
+ for (const callback of afterRollback.splice(0)) {
26
+ parent.afterRollback(callback);
27
+ }
28
+ }
29
+ //# sourceMappingURL=transaction.savepoint.js.map
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Transaction error types.
3
3
  */
4
- export { TransactionError } from "./transactionError.base.js";
4
+ export { TransactionError, type TransactionErrorOptions, } from "./transactionError.base.js";
5
5
  export { TransactionStateError, TransactionTimeoutError, TransactionCommitError, TransactionRollbackError, TransactionAdapterError, TransactionPropagationError, TransactionIsolationError, SavepointError, TransactionRequiredError, TransactionUnexpectedError, TransactionCapabilityError, } from "./transactionError.types.js";
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Transaction error types.
3
3
  */
4
- export { TransactionError } from "./transactionError.base.js";
4
+ export { TransactionError, } from "./transactionError.base.js";
5
5
  export { TransactionStateError, TransactionTimeoutError, TransactionCommitError, TransactionRollbackError, TransactionAdapterError, TransactionPropagationError, TransactionIsolationError, SavepointError, TransactionRequiredError, TransactionUnexpectedError, TransactionCapabilityError, } from "./transactionError.types.js";
6
6
  //# sourceMappingURL=index.js.map
@@ -1,15 +1,9 @@
1
1
  /**
2
2
  * Base transaction error class.
3
+ *
4
+ * Owned by `@zudojs/errors` (round 10 INF-16) and re-exported here, so
5
+ * `instanceof TransactionError` matches whichever package a caller imports
6
+ * it from. Name, constructor, `code` defaults and category are unchanged.
3
7
  */
4
- import { BaseError, ErrorCode, type ErrorMetadata } from "@zudojs/errors";
5
- /**
6
- * Base error for all transaction-related failures.
7
- */
8
- export declare class TransactionError extends BaseError {
9
- constructor(message: string, options?: {
10
- readonly code?: ErrorCode;
11
- readonly metadata?: ErrorMetadata;
12
- readonly cause?: unknown;
13
- });
14
- }
8
+ export { TransactionError, type TransactionErrorOptions } from "@zudojs/errors";
15
9
  //# sourceMappingURL=transactionError.base.d.ts.map
@@ -1,19 +1,9 @@
1
1
  /**
2
2
  * Base transaction error class.
3
+ *
4
+ * Owned by `@zudojs/errors` (round 10 INF-16) and re-exported here, so
5
+ * `instanceof TransactionError` matches whichever package a caller imports
6
+ * it from. Name, constructor, `code` defaults and category are unchanged.
3
7
  */
4
- import { BaseError, ErrorCode, ErrorCategory, ErrorSeverity, } from "@zudojs/errors";
5
- /**
6
- * Base error for all transaction-related failures.
7
- */
8
- export class TransactionError extends BaseError {
9
- constructor(message, options) {
10
- super(message, {
11
- code: options?.code ?? ErrorCode.OPERATION_FAILED,
12
- category: ErrorCategory.DATABASE,
13
- severity: ErrorSeverity.ERROR,
14
- metadata: options?.metadata,
15
- cause: options?.cause,
16
- });
17
- }
18
- }
8
+ export { TransactionError } from "@zudojs/errors";
19
9
  //# sourceMappingURL=transactionError.base.js.map
@@ -1,74 +1,9 @@
1
1
  /**
2
2
  * Specific transaction error subclasses.
3
+ *
4
+ * Owned by `@zudojs/errors` (round 10 INF-16) and re-exported here with the
5
+ * same names, constructor signatures and codes, so `instanceof` checks match
6
+ * across both import paths.
3
7
  */
4
- import { TransactionError } from "./transactionError.base.js";
5
- /**
6
- * Transaction is in an invalid state for the requested operation.
7
- */
8
- export declare class TransactionStateError extends TransactionError {
9
- constructor(state: string, operation: string);
10
- }
11
- /**
12
- * Transaction exceeded its timeout.
13
- */
14
- export declare class TransactionTimeoutError extends TransactionError {
15
- constructor(transactionId: string, timeoutMs: number);
16
- }
17
- /**
18
- * Transaction commit failed.
19
- */
20
- export declare class TransactionCommitError extends TransactionError {
21
- constructor(transactionId: string, cause?: unknown);
22
- }
23
- /**
24
- * Transaction rollback failed.
25
- */
26
- export declare class TransactionRollbackError extends TransactionError {
27
- constructor(transactionId: string, options?: {
28
- readonly cause?: unknown;
29
- readonly originalError?: unknown;
30
- });
31
- }
32
- /**
33
- * The underlying adapter threw an error.
34
- */
35
- export declare class TransactionAdapterError extends TransactionError {
36
- constructor(message: string, cause?: unknown);
37
- }
38
- /**
39
- * Propagation strategy violation.
40
- */
41
- export declare class TransactionPropagationError extends TransactionError {
42
- constructor(message: string);
43
- }
44
- /**
45
- * Required isolation level is not supported by the adapter.
46
- */
47
- export declare class TransactionIsolationError extends TransactionError {
48
- constructor(level: string);
49
- }
50
- /**
51
- * Savepoint operation failed.
52
- */
53
- export declare class SavepointError extends TransactionError {
54
- constructor(message: string, cause?: unknown);
55
- }
56
- /**
57
- * A transaction is required but none exists.
58
- */
59
- export declare class TransactionRequiredError extends TransactionError {
60
- constructor();
61
- }
62
- /**
63
- * A transaction exists but none was expected.
64
- */
65
- export declare class TransactionUnexpectedError extends TransactionError {
66
- constructor();
67
- }
68
- /**
69
- * The adapter does not support the requested capability.
70
- */
71
- export declare class TransactionCapabilityError extends TransactionError {
72
- constructor(capability: string);
73
- }
8
+ export { TransactionStateError, TransactionTimeoutError, TransactionCommitError, TransactionRollbackError, TransactionAdapterError, TransactionPropagationError, TransactionIsolationError, SavepointError, TransactionRequiredError, TransactionUnexpectedError, TransactionCapabilityError, } from "@zudojs/errors";
74
9
  //# sourceMappingURL=transactionError.types.d.ts.map
@@ -1,129 +1,9 @@
1
1
  /**
2
2
  * Specific transaction error subclasses.
3
+ *
4
+ * Owned by `@zudojs/errors` (round 10 INF-16) and re-exported here with the
5
+ * same names, constructor signatures and codes, so `instanceof` checks match
6
+ * across both import paths.
3
7
  */
4
- import { ErrorCode } from "@zudojs/errors";
5
- import { TransactionError } from "./transactionError.base.js";
6
- /**
7
- * Transaction is in an invalid state for the requested operation.
8
- */
9
- export class TransactionStateError extends TransactionError {
10
- constructor(state, operation) {
11
- super(`Cannot ${operation} transaction in state "${state}"`, {
12
- code: ErrorCode.LIFECYCLE_STATE,
13
- metadata: { state, operation },
14
- });
15
- }
16
- }
17
- /**
18
- * Transaction exceeded its timeout.
19
- */
20
- export class TransactionTimeoutError extends TransactionError {
21
- constructor(transactionId, timeoutMs) {
22
- super(`Transaction "${transactionId}" timed out after ${timeoutMs}ms`, {
23
- code: ErrorCode.TIMEOUT,
24
- metadata: { transactionId, timeoutMs },
25
- });
26
- }
27
- }
28
- /**
29
- * Transaction commit failed.
30
- */
31
- export class TransactionCommitError extends TransactionError {
32
- constructor(transactionId, cause) {
33
- super(`Transaction "${transactionId}" commit failed`, {
34
- code: ErrorCode.DATABASE_TRANSACTION,
35
- cause,
36
- metadata: { transactionId },
37
- });
38
- }
39
- }
40
- /**
41
- * Transaction rollback failed.
42
- */
43
- export class TransactionRollbackError extends TransactionError {
44
- constructor(transactionId, options) {
45
- super(`Transaction "${transactionId}" rollback failed`, {
46
- code: ErrorCode.DATABASE_TRANSACTION,
47
- cause: options?.cause,
48
- metadata: {
49
- transactionId,
50
- originalError: options?.originalError instanceof Error
51
- ? options.originalError.message
52
- : String(options?.originalError ?? "unknown"),
53
- },
54
- });
55
- }
56
- }
57
- /**
58
- * The underlying adapter threw an error.
59
- */
60
- export class TransactionAdapterError extends TransactionError {
61
- constructor(message, cause) {
62
- super(message, {
63
- code: ErrorCode.ADAPTER_OPERATION_FAILED,
64
- cause,
65
- });
66
- }
67
- }
68
- /**
69
- * Propagation strategy violation.
70
- */
71
- export class TransactionPropagationError extends TransactionError {
72
- constructor(message) {
73
- super(message, { code: ErrorCode.VALIDATION_FAILED });
74
- }
75
- }
76
- /**
77
- * Required isolation level is not supported by the adapter.
78
- */
79
- export class TransactionIsolationError extends TransactionError {
80
- constructor(level) {
81
- super(`Isolation level "${level}" is not supported by the adapter`, {
82
- code: ErrorCode.VALIDATION_FAILED,
83
- metadata: { level },
84
- });
85
- }
86
- }
87
- /**
88
- * Savepoint operation failed.
89
- */
90
- export class SavepointError extends TransactionError {
91
- constructor(message, cause) {
92
- super(message, {
93
- code: ErrorCode.OPERATION_FAILED,
94
- cause,
95
- });
96
- }
97
- }
98
- /**
99
- * A transaction is required but none exists.
100
- */
101
- export class TransactionRequiredError extends TransactionError {
102
- constructor() {
103
- super("A transaction is required but none exists", {
104
- code: ErrorCode.PRECONDITION_FAILED,
105
- });
106
- }
107
- }
108
- /**
109
- * A transaction exists but none was expected.
110
- */
111
- export class TransactionUnexpectedError extends TransactionError {
112
- constructor() {
113
- super("A transaction already exists but none was expected", {
114
- code: ErrorCode.CONFLICT,
115
- });
116
- }
117
- }
118
- /**
119
- * The adapter does not support the requested capability.
120
- */
121
- export class TransactionCapabilityError extends TransactionError {
122
- constructor(capability) {
123
- super(`Adapter does not support: ${capability}`, {
124
- code: ErrorCode.NOT_IMPLEMENTED,
125
- metadata: { capability },
126
- });
127
- }
128
- }
8
+ export { TransactionStateError, TransactionTimeoutError, TransactionCommitError, TransactionRollbackError, TransactionAdapterError, TransactionPropagationError, TransactionIsolationError, SavepointError, TransactionRequiredError, TransactionUnexpectedError, TransactionCapabilityError, } from "@zudojs/errors";
129
9
  //# sourceMappingURL=transactionError.types.js.map
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@zudojs/transactions",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Transaction lifecycle and coordination with state machine, AsyncLocalStorage context propagation, savepoints, hooks, and adapter abstraction.",
5
5
  "license": "MIT",
6
+ "author": {
7
+ "name": "Oluwayemi Oyinlola",
8
+ "url": "https://github.com/oyinlola-tech"
9
+ },
6
10
  "type": "module",
7
11
  "main": "./dist/index.js",
8
12
  "module": "./dist/index.js",
@@ -21,7 +25,7 @@
21
25
  "!dist/.tsbuildinfo"
22
26
  ],
23
27
  "dependencies": {
24
- "@zudojs/errors": "1.0.0"
28
+ "@zudojs/errors": "1.1.0"
25
29
  },
26
30
  "engines": {
27
31
  "node": ">=24.0.0"