@kronos-ts/knex 0.2.4 → 0.3.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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/knex-token-store.d.ts.map +1 -1
- package/dist/knex-token-store.js +4 -9
- package/dist/knex-token-store.js.map +1 -1
- package/dist/knex-transaction-manager.d.ts +21 -1
- package/dist/knex-transaction-manager.d.ts.map +1 -1
- package/dist/knex-transaction-manager.js +20 -2
- package/dist/knex-transaction-manager.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +1 -0
- package/src/knex-token-store.ts +9 -8
- package/src/knex-transaction-manager.ts +40 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { knexTransactionManager, type KnexInstanceLike, type KnexTransaction, } from "./knex-transaction-manager.js";
|
|
1
|
+
export { knexTransactionManager, type KnexInstanceLike, type KnexTransaction, type KnexTransactionManagerOptions, } from "./knex-transaction-manager.js";
|
|
2
2
|
export { knexTokenStore, type KnexQueryable, } from "./knex-token-store.js";
|
|
3
3
|
export { knexDeadLetterQueue, type KnexDeadLetterQueueConfig, } from "./knex-dead-letter-queue.js";
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,6BAA6B,GACnC,MAAM,+BAA+B,CAAA;AAEtC,OAAO,EACL,cAAc,EACd,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,mBAAmB,EACnB,KAAK,yBAAyB,GAC/B,MAAM,6BAA6B,CAAA"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,GAIvB,MAAM,+BAA+B,CAAA;AAEtC,OAAO,EACL,cAAc,GAEf,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,mBAAmB,GAEpB,MAAM,6BAA6B,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knex-token-store.d.ts","sourceRoot":"","sources":["../src/knex-token-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"knex-token-store.d.ts","sourceRoot":"","sources":["../src/knex-token-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,sBAAsB,CAAA;AAsBrE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,CAAA;IACxB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,CAAA;CAC1C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,aAAa,EACnB,OAAO,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD,UAAU,CA+GZ"}
|
package/dist/knex-token-store.js
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import { getActiveTransaction, UnableToClaimTokenError,
|
|
1
|
+
import { getActiveTransaction, UnableToClaimTokenError, serializeToken as serializeTokenData, deserializeToken as deserializeTokenData, } from "@kronos-ts/messaging";
|
|
2
2
|
function serializeToken(token) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
token: JSON.stringify({ position: token.position().toString() }),
|
|
6
|
-
};
|
|
3
|
+
const { type, data } = serializeTokenData(token);
|
|
4
|
+
return { token_type: type, token: data };
|
|
7
5
|
}
|
|
8
6
|
function deserializeToken(tokenType, token) {
|
|
9
|
-
|
|
10
|
-
return undefined;
|
|
11
|
-
const data = JSON.parse(token);
|
|
12
|
-
return globalSequenceToken(BigInt(data.position));
|
|
7
|
+
return deserializeTokenData(tokenType, token);
|
|
13
8
|
}
|
|
14
9
|
function nowIso() {
|
|
15
10
|
return new Date().toISOString();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knex-token-store.js","sourceRoot":"","sources":["../src/knex-token-store.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"knex-token-store.js","sourceRoot":"","sources":["../src/knex-token-store.ts"],"names":[],"mappings":"AACA,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,cAAc,IAAI,kBAAkB,EACpC,gBAAgB,IAAI,oBAAoB,GACzC,MAAM,sBAAsB,CAAA;AAG7B,SAAS,cAAc,CAAC,KAAoB;IAC1C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAA;IAChD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AAC1C,CAAC;AAED,SAAS,gBAAgB,CAAC,SAAwB,EAAE,KAAoB;IACtE,OAAO,oBAAoB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;AAC/C,CAAC;AAED,SAAS,MAAM;IACb,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;AACjC,CAAC;AAUD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAmB,EACnB,OAAyD;IAEzD,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,KAAK,CAAA;IACvD,MAAM,KAAK,GAAG,OAAO,EAAE,SAAS,IAAI,sBAAsB,CAAA;IAE1D,SAAS,OAAO;QACd,OAAO,oBAAoB,EAAmB,IAAI,IAAI,CAAA;IACxD,CAAC;IAED,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,aAAa,EAAE,OAAO,EAAE,KAAK;YACvC,MAAM,CAAC,GAAG,OAAO,EAAE,CAAA;YACnB,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;YAC9D,MAAM,CAAC,CAAC,GAAG,CACT;;sGAE8F,EAC9F,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAClG,CAAA;QACH,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO;YAC9B,MAAM,CAAC,GAAG,OAAO,EAAE,CAAA;YACnB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC;iBACvB,KAAK,CAAC,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;iBACjD,KAAK,EAAE,CAAA;YACV,IAAI,CAAC,GAAG;gBAAE,OAAO,SAAS,CAAA;YAC1B,OAAO,gBAAgB,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;QACpD,CAAC;QAED,KAAK,CAAC,kBAAkB,CAAC,aAAa,EAAE,YAAY;YAClD,MAAM,CAAC,GAAG,OAAO,EAAE,CAAA;YACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,MAAM,CAAC,CAAC,GAAG,CACT;;4DAEkD,EAClD,CAAC,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAC1B,CAAA;YACH,CAAC;QACH,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO;YAC9C,MAAM,CAAC,GAAG,OAAO,EAAE,CAAA;YACnB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC;iBACvB,KAAK,CAAC,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;iBACjD,KAAK,EAAE,CAAA;YAEV,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;oBACpB,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;oBAC/C,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO;iBACnE,CAAC,CAAA;gBACF,OAAO,SAAS,CAAA;YAClB,CAAC;YAED,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,SAAS;gBAC5C,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,CAAA;YAEnE,IAAI,GAAG,CAAC,KAAK,KAAK,OAAO,IAAI,SAAS,EAAE,CAAC;gBACvC,MAAM,CAAC,CAAC,KAAK,CAAC;qBACX,KAAK,CAAC,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;qBACjD,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,CAAA;gBAClD,OAAO,gBAAgB,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;YACpD,CAAC;YAED,MAAM,IAAI,uBAAuB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAA;QAC3D,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO;YAC/C,MAAM,CAAC,GAAG,OAAO,EAAE,CAAA;YACnB,MAAM,CAAC,CAAC,KAAK,CAAC;iBACX,KAAK,CAAC,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;iBACjE,MAAM,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,CAAA;QACpC,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO;YAChD,MAAM,CAAC,GAAG,OAAO,EAAE,CAAA;YACnB,MAAM,CAAC,CAAC,KAAK,CAAC;iBACX,KAAK,CAAC,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;iBACjE,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC7C,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,aAAa;YAC/B,MAAM,CAAC,GAAG,OAAO,EAAE,CAAA;YACnB,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC;iBACxB,KAAK,CAAC,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC;iBACxC,MAAM,CAAC,SAAS,CAAC;iBACjB,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;YAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;QACxC,CAAC;QAED,KAAK,CAAC,sBAAsB,CAAC,aAAa;YACxC,MAAM,CAAC,GAAG,OAAO,EAAE,CAAA;YACnB,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,CAAC,CAAC,WAAW,EAAE,CAAA;YAClE,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC;iBACxB,KAAK,CAAC,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC;iBACxC,KAAK,CAAC;gBACL,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;YAC3D,CAAC,CAAC;iBACD,MAAM,CAAC,SAAS,CAAC;iBACjB,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;YAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;QACxC,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,aAAa,EAAE,OAAO;YACtC,MAAM,CAAC,GAAG,OAAO,EAAE,CAAA;YACnB,MAAM,CAAC,CAAC,KAAK,CAAC;iBACX,KAAK,CAAC,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;iBACjD,MAAM,EAAE,CAAA;QACb,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -9,6 +9,26 @@ export interface KnexInstanceLike {
|
|
|
9
9
|
* The Knex transaction object.
|
|
10
10
|
*/
|
|
11
11
|
export type KnexTransaction = any;
|
|
12
|
+
export interface KnexTransactionManagerOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Runs once on every transaction, right after it opens and before the UoW
|
|
15
|
+
* gets the handle. This is where a Postgres-backed deployment arms session
|
|
16
|
+
* GUCs — chiefly `idle_in_transaction_session_timeout` — so a stalled UoW
|
|
17
|
+
* (e.g. a hung dead-letter drain whose replay never returns) is aborted by
|
|
18
|
+
* the database instead of pinning a connection indefinitely. The Postgres
|
|
19
|
+
* adapter does this for its own transactions; this hook is the equivalent
|
|
20
|
+
* seam for the (DB-agnostic) Knex/Drizzle path. No-op by default.
|
|
21
|
+
*
|
|
22
|
+
* Example (pg client):
|
|
23
|
+
* ```ts
|
|
24
|
+
* knexTransactionManager(knex, {
|
|
25
|
+
* onBeginTransaction: (trx) => trx.raw(
|
|
26
|
+
* "SET LOCAL idle_in_transaction_session_timeout = 30000"),
|
|
27
|
+
* })
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
readonly onBeginTransaction?: (tx: KnexTransaction) => Promise<void>;
|
|
31
|
+
}
|
|
12
32
|
/**
|
|
13
33
|
* Creates a TransactionManager for Knex.
|
|
14
34
|
*
|
|
@@ -27,5 +47,5 @@ export type KnexTransaction = any;
|
|
|
27
47
|
* const txManager = knexTransactionManager(knex)
|
|
28
48
|
* ```
|
|
29
49
|
*/
|
|
30
|
-
export declare function knexTransactionManager(knex: KnexInstanceLike): TransactionManager<KnexTransaction>;
|
|
50
|
+
export declare function knexTransactionManager(knex: KnexInstanceLike, options?: KnexTransactionManagerOptions): TransactionManager<KnexTransaction>;
|
|
31
51
|
//# sourceMappingURL=knex-transaction-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knex-transaction-manager.d.ts","sourceRoot":"","sources":["../src/knex-transaction-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAE9D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CACzD;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,GAAG,CAAA;AAEjC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"knex-transaction-manager.d.ts","sourceRoot":"","sources":["../src/knex-transaction-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAE9D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CACzD;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,GAAG,CAAA;AAEjC,MAAM,WAAW,6BAA6B;IAC5C;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACrE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,gBAAgB,EACtB,OAAO,GAAE,6BAAkC,GAC1C,kBAAkB,CAAC,eAAe,CAAC,CA4DrC"}
|
|
@@ -16,23 +16,41 @@
|
|
|
16
16
|
* const txManager = knexTransactionManager(knex)
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
|
-
export function knexTransactionManager(knex) {
|
|
19
|
+
export function knexTransactionManager(knex, options = {}) {
|
|
20
|
+
const { onBeginTransaction } = options;
|
|
20
21
|
return {
|
|
21
22
|
async begin() {
|
|
22
23
|
let resolveTx;
|
|
24
|
+
let rejectTx;
|
|
23
25
|
let resolveCompletion;
|
|
24
26
|
let rejectCompletion;
|
|
25
|
-
const txReady = new Promise((resolve) => {
|
|
27
|
+
const txReady = new Promise((resolve, reject) => {
|
|
26
28
|
resolveTx = resolve;
|
|
29
|
+
rejectTx = reject;
|
|
27
30
|
});
|
|
28
31
|
const completionSignal = new Promise((resolve, reject) => {
|
|
29
32
|
resolveCompletion = resolve;
|
|
30
33
|
rejectCompletion = reject;
|
|
31
34
|
});
|
|
32
35
|
const txPromise = knex.transaction(async (trx) => {
|
|
36
|
+
// Arm session settings (e.g. idle-in-transaction timeout) before the
|
|
37
|
+
// UoW gets the handle, so begin() only resolves once the tx is bounded.
|
|
38
|
+
if (onBeginTransaction) {
|
|
39
|
+
try {
|
|
40
|
+
await onBeginTransaction(trx);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
rejectTx(err);
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
33
47
|
resolveTx(trx);
|
|
34
48
|
await completionSignal;
|
|
35
49
|
});
|
|
50
|
+
// If knex.transaction() rejects before onBeginTransaction runs (e.g. the
|
|
51
|
+
// pool can't hand out a connection), make begin() reject instead of
|
|
52
|
+
// hanging on txReady forever.
|
|
53
|
+
txPromise.catch(rejectTx);
|
|
36
54
|
const tx = await txReady;
|
|
37
55
|
tx.__kronos_commit = resolveCompletion;
|
|
38
56
|
tx.__kronos_rollback = rejectCompletion;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knex-transaction-manager.js","sourceRoot":"","sources":["../src/knex-transaction-manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"knex-transaction-manager.js","sourceRoot":"","sources":["../src/knex-transaction-manager.ts"],"names":[],"mappings":"AAmCA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAsB,EACtB,UAAyC,EAAE;IAE3C,MAAM,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAA;IACtC,OAAO;QACL,KAAK,CAAC,KAAK;YACT,IAAI,SAAyC,CAAA;YAC7C,IAAI,QAAmC,CAAA;YACvC,IAAI,iBAA8B,CAAA;YAClC,IAAI,gBAA2C,CAAA;YAE/C,MAAM,OAAO,GAAG,IAAI,OAAO,CAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC/D,SAAS,GAAG,OAAO,CAAA;gBACnB,QAAQ,GAAG,MAAM,CAAA;YACnB,CAAC,CAAC,CAAA;YAEF,MAAM,gBAAgB,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC7D,iBAAiB,GAAG,OAAO,CAAA;gBAC3B,gBAAgB,GAAG,MAAM,CAAA;YAC3B,CAAC,CAAC,CAAA;YAEF,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;gBAC/C,qEAAqE;gBACrE,wEAAwE;gBACxE,IAAI,kBAAkB,EAAE,CAAC;oBACvB,IAAI,CAAC;wBACH,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAA;oBAC/B,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,QAAQ,CAAC,GAAG,CAAC,CAAA;wBACb,MAAM,GAAG,CAAA;oBACX,CAAC;gBACH,CAAC;gBACD,SAAS,CAAC,GAAG,CAAC,CAAA;gBACd,MAAM,gBAAgB,CAAA;YACxB,CAAC,CAAC,CAAA;YACF,yEAAyE;YACzE,oEAAoE;YACpE,8BAA8B;YAC9B,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;YAEzB,MAAM,EAAE,GAAG,MAAM,OAAO,CACvB;YAAC,EAAU,CAAC,eAAe,GAAG,iBAAiB,CAC/C;YAAC,EAAU,CAAC,iBAAiB,GAAG,gBAAgB,CAChD;YAAC,EAAU,CAAC,kBAAkB,GAAG,SAAS,CAAA;YAE3C,OAAO,EAAE,CAAA;QACX,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,EAAmB;YAC9B,MAAM,MAAM,GAAI,EAAU,CAAC,eAA6B,CAAA;YACxD,MAAM,SAAS,GAAI,EAAU,CAAC,kBAAmC,CAAA;YACjE,MAAM,EAAE,CAAA;YACR,MAAM,SAAS,CAAA;QACjB,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,EAAmB;YAChC,MAAM,QAAQ,GAAI,EAAU,CAAC,iBAA6C,CAAA;YAC1E,MAAM,SAAS,GAAI,EAAU,CAAC,kBAAmC,CAAA;YACjE,QAAQ,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAA;YAC9C,IAAI,CAAC;gBAAC,MAAM,SAAS,CAAA;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;QAClD,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kronos-ts/knex",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Knex extension for Kronos.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@kronos-ts/common": "0.1.1",
|
|
52
|
-
"@kronos-ts/messaging": "0.
|
|
52
|
+
"@kronos-ts/messaging": "0.9.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"knex": ">=3.0.0"
|
package/src/index.ts
CHANGED
package/src/knex-token-store.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import type { TokenStore, TrackingToken } from "@kronos-ts/messaging"
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getActiveTransaction,
|
|
4
|
+
UnableToClaimTokenError,
|
|
5
|
+
serializeToken as serializeTokenData,
|
|
6
|
+
deserializeToken as deserializeTokenData,
|
|
7
|
+
} from "@kronos-ts/messaging"
|
|
3
8
|
import type { KnexTransaction } from "./knex-transaction-manager.js"
|
|
4
9
|
|
|
5
10
|
function serializeToken(token: TrackingToken): { token_type: string; token: string } {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
token: JSON.stringify({ position: token.position().toString() }),
|
|
9
|
-
}
|
|
11
|
+
const { type, data } = serializeTokenData(token)
|
|
12
|
+
return { token_type: type, token: data }
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
function deserializeToken(tokenType: string | null, token: string | null): TrackingToken | undefined {
|
|
13
|
-
|
|
14
|
-
const data = JSON.parse(token)
|
|
15
|
-
return globalSequenceToken(BigInt(data.position))
|
|
16
|
+
return deserializeTokenData(tokenType, token)
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
function nowIso(): string {
|
|
@@ -12,6 +12,27 @@ export interface KnexInstanceLike {
|
|
|
12
12
|
*/
|
|
13
13
|
export type KnexTransaction = any
|
|
14
14
|
|
|
15
|
+
export interface KnexTransactionManagerOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Runs once on every transaction, right after it opens and before the UoW
|
|
18
|
+
* gets the handle. This is where a Postgres-backed deployment arms session
|
|
19
|
+
* GUCs — chiefly `idle_in_transaction_session_timeout` — so a stalled UoW
|
|
20
|
+
* (e.g. a hung dead-letter drain whose replay never returns) is aborted by
|
|
21
|
+
* the database instead of pinning a connection indefinitely. The Postgres
|
|
22
|
+
* adapter does this for its own transactions; this hook is the equivalent
|
|
23
|
+
* seam for the (DB-agnostic) Knex/Drizzle path. No-op by default.
|
|
24
|
+
*
|
|
25
|
+
* Example (pg client):
|
|
26
|
+
* ```ts
|
|
27
|
+
* knexTransactionManager(knex, {
|
|
28
|
+
* onBeginTransaction: (trx) => trx.raw(
|
|
29
|
+
* "SET LOCAL idle_in_transaction_session_timeout = 30000"),
|
|
30
|
+
* })
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
readonly onBeginTransaction?: (tx: KnexTransaction) => Promise<void>
|
|
34
|
+
}
|
|
35
|
+
|
|
15
36
|
/**
|
|
16
37
|
* Creates a TransactionManager for Knex.
|
|
17
38
|
*
|
|
@@ -32,15 +53,19 @@ export type KnexTransaction = any
|
|
|
32
53
|
*/
|
|
33
54
|
export function knexTransactionManager(
|
|
34
55
|
knex: KnexInstanceLike,
|
|
56
|
+
options: KnexTransactionManagerOptions = {},
|
|
35
57
|
): TransactionManager<KnexTransaction> {
|
|
58
|
+
const { onBeginTransaction } = options
|
|
36
59
|
return {
|
|
37
60
|
async begin(): Promise<KnexTransaction> {
|
|
38
61
|
let resolveTx!: (tx: KnexTransaction) => void
|
|
62
|
+
let rejectTx!: (error: unknown) => void
|
|
39
63
|
let resolveCompletion!: () => void
|
|
40
64
|
let rejectCompletion!: (error: unknown) => void
|
|
41
65
|
|
|
42
|
-
const txReady = new Promise<KnexTransaction>((resolve) => {
|
|
66
|
+
const txReady = new Promise<KnexTransaction>((resolve, reject) => {
|
|
43
67
|
resolveTx = resolve
|
|
68
|
+
rejectTx = reject
|
|
44
69
|
})
|
|
45
70
|
|
|
46
71
|
const completionSignal = new Promise<void>((resolve, reject) => {
|
|
@@ -49,9 +74,23 @@ export function knexTransactionManager(
|
|
|
49
74
|
})
|
|
50
75
|
|
|
51
76
|
const txPromise = knex.transaction(async (trx) => {
|
|
77
|
+
// Arm session settings (e.g. idle-in-transaction timeout) before the
|
|
78
|
+
// UoW gets the handle, so begin() only resolves once the tx is bounded.
|
|
79
|
+
if (onBeginTransaction) {
|
|
80
|
+
try {
|
|
81
|
+
await onBeginTransaction(trx)
|
|
82
|
+
} catch (err) {
|
|
83
|
+
rejectTx(err)
|
|
84
|
+
throw err
|
|
85
|
+
}
|
|
86
|
+
}
|
|
52
87
|
resolveTx(trx)
|
|
53
88
|
await completionSignal
|
|
54
89
|
})
|
|
90
|
+
// If knex.transaction() rejects before onBeginTransaction runs (e.g. the
|
|
91
|
+
// pool can't hand out a connection), make begin() reject instead of
|
|
92
|
+
// hanging on txReady forever.
|
|
93
|
+
txPromise.catch(rejectTx)
|
|
55
94
|
|
|
56
95
|
const tx = await txReady
|
|
57
96
|
;(tx as any).__kronos_commit = resolveCompletion
|