@hexaijs/sqlite 0.3.0 → 0.4.0

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
@@ -104,6 +104,39 @@ await db.run("UPDATE orders SET status = ? WHERE id = ?", ["confirmed", orderId]
104
104
 
105
105
  Note: `getClient()` throws an error if called outside of a `scope()` or `wrap()` call.
106
106
 
107
+ ### Transaction Lifecycle Hooks
108
+
109
+ Register callbacks that execute at specific points in the transaction lifecycle:
110
+
111
+ ```typescript
112
+ await unitOfWork.scope(async () => {
113
+ unitOfWork.beforeCommit(async () => {
114
+ // Validate before committing
115
+ });
116
+
117
+ unitOfWork.afterCommit(async () => {
118
+ // Notify after successful commit
119
+ });
120
+
121
+ unitOfWork.afterRollback(async () => {
122
+ // Clean up on failure
123
+ });
124
+
125
+ const db = unitOfWork.getClient();
126
+ await db.run("INSERT INTO orders (id, status) VALUES (?, ?)", [orderId, "pending"]);
127
+ });
128
+ ```
129
+
130
+ Hooks follow the same semantics as `@hexaijs/postgres`:
131
+
132
+ | Hook | When | On failure |
133
+ |------|------|------------|
134
+ | `beforeCommit` | Before COMMIT | Transaction rolls back instead |
135
+ | `afterCommit` | After COMMIT | Best-effort (errors → `AggregateError`) |
136
+ | `afterRollback` | After ROLLBACK | Best-effort (errors → `AggregateError`) |
137
+
138
+ Hooks are scope-local and can only be registered inside an active `scope()`.
139
+
107
140
  ### Nested Transactions
108
141
 
109
142
  Nested `scope()` calls participate in the same transaction:
package/dist/index.d.ts CHANGED
@@ -1,13 +1,17 @@
1
1
  import { Database } from 'sqlite';
2
- import { UnitOfWork } from '@hexaijs/core';
2
+ import { UnitOfWork, TransactionHook } from '@hexaijs/core';
3
3
 
4
4
  declare class SqliteUnitOfWork implements UnitOfWork<Database> {
5
5
  private db;
6
6
  private static transactions;
7
7
  constructor(db: Database);
8
8
  getClient(): Database;
9
+ beforeCommit(hook: TransactionHook): void;
10
+ afterCommit(hook: TransactionHook): void;
11
+ afterRollback(hook: TransactionHook): void;
9
12
  scope<T>(fn: () => Promise<T>): Promise<T>;
10
13
  wrap<T>(fn: (client: Database) => Promise<T>): Promise<T>;
14
+ private getRequiredState;
11
15
  }
12
16
 
13
17
  export { SqliteUnitOfWork };
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { TransactionHooks } from '@hexaijs/core';
2
+
1
3
  // src/sqlite-unit-of-work.ts
2
4
  var SqliteUnitOfWork = class _SqliteUnitOfWork {
3
5
  constructor(db) {
@@ -5,7 +7,8 @@ var SqliteUnitOfWork = class _SqliteUnitOfWork {
5
7
  if (!_SqliteUnitOfWork.transactions.has(db)) {
6
8
  _SqliteUnitOfWork.transactions.set(db, {
7
9
  level: 0,
8
- aborted: false
10
+ aborted: false,
11
+ hooks: new TransactionHooks()
9
12
  });
10
13
  }
11
14
  }
@@ -17,6 +20,18 @@ var SqliteUnitOfWork = class _SqliteUnitOfWork {
17
20
  }
18
21
  return this.db;
19
22
  }
23
+ beforeCommit(hook) {
24
+ const current = this.getRequiredState("beforeCommit");
25
+ current.hooks.addBeforeCommit(hook);
26
+ }
27
+ afterCommit(hook) {
28
+ const current = this.getRequiredState("afterCommit");
29
+ current.hooks.addAfterCommit(hook);
30
+ }
31
+ afterRollback(hook) {
32
+ const current = this.getRequiredState("afterRollback");
33
+ current.hooks.addAfterRollback(hook);
34
+ }
20
35
  async scope(fn) {
21
36
  return this.wrap(fn);
22
37
  }
@@ -25,23 +40,50 @@ var SqliteUnitOfWork = class _SqliteUnitOfWork {
25
40
  if (++current.level === 1) {
26
41
  await this.db.run("BEGIN TRANSACTION");
27
42
  }
43
+ let abortError;
28
44
  try {
29
45
  return await fn(this.db);
30
46
  } catch (e) {
31
47
  if (!current.aborted) {
32
48
  current.aborted = true;
33
49
  }
50
+ abortError = e;
34
51
  throw e;
35
52
  } finally {
36
53
  if (--current.level === 0) {
37
- if (current.aborted) {
38
- await this.db.run("ROLLBACK");
54
+ const hooks = current.hooks;
55
+ const wasAborted = current.aborted;
56
+ current.hooks = new TransactionHooks();
57
+ current.aborted = false;
58
+ if (wasAborted) {
59
+ await hooks.executeRollback(
60
+ async () => {
61
+ await this.db.run("ROLLBACK");
62
+ },
63
+ abortError
64
+ );
39
65
  } else {
40
- await this.db.run("COMMIT");
66
+ await hooks.executeCommit(
67
+ async () => {
68
+ await this.db.run("COMMIT");
69
+ },
70
+ async () => {
71
+ await this.db.run("ROLLBACK");
72
+ }
73
+ );
41
74
  }
42
75
  }
43
76
  }
44
77
  }
78
+ getRequiredState(hookName) {
79
+ const current = _SqliteUnitOfWork.transactions.get(this.db);
80
+ if (!current || current.level === 0) {
81
+ throw new Error(
82
+ `Cannot register ${hookName} hook outside of a transaction scope`
83
+ );
84
+ }
85
+ return current;
86
+ }
45
87
  };
46
88
 
47
89
  export { SqliteUnitOfWork };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sqlite-unit-of-work.ts"],"names":[],"mappings":";AAIO,IAAM,gBAAA,GAAN,MAAM,iBAAA,CAAiD;AAAA,EAS1D,YAAoB,EAAA,EAAc;AAAd,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AAChB,IAAA,IAAI,CAAC,iBAAA,CAAiB,YAAA,CAAa,GAAA,CAAI,EAAE,CAAA,EAAG;AACxC,MAAA,iBAAA,CAAiB,YAAA,CAAa,IAAI,EAAA,EAAI;AAAA,QAClC,KAAA,EAAO,CAAA;AAAA,QACP,OAAA,EAAS;AAAA,OACZ,CAAA;AAAA,IACL;AAAA,EACJ;AAAA,EAfA,OAAe,YAAA,mBAAe,IAAI,OAAA,EAMhC;AAAA,EAWF,SAAA,GAAsB;AAClB,IAAA,MAAM,OAAA,GAAU,iBAAA,CAAiB,YAAA,CAAa,GAAA,CAAI,KAAK,EAAE,CAAA;AACzD,IAAA,IAAI,CAAC,OAAA,IAAW,OAAA,CAAQ,KAAA,KAAU,CAAA,EAAG;AACjC,MAAA,MAAM,IAAI,MAAM,0BAA0B,CAAA;AAAA,IAC9C;AACA,IAAA,OAAO,IAAA,CAAK,EAAA;AAAA,EAChB;AAAA,EAEA,MAAM,MAAS,EAAA,EAAkC;AAC7C,IAAA,OAAO,IAAA,CAAK,KAAK,EAAE,CAAA;AAAA,EACvB;AAAA,EAEA,MAAM,KAAQ,EAAA,EAAkD;AAC5D,IAAA,MAAM,OAAA,GAAU,iBAAA,CAAiB,YAAA,CAAa,GAAA,CAAI,KAAK,EAAE,CAAA;AACzD,IAAA,IAAI,EAAE,OAAA,CAAQ,KAAA,KAAU,CAAA,EAAG;AACvB,MAAA,MAAM,IAAA,CAAK,EAAA,CAAG,GAAA,CAAI,mBAAmB,CAAA;AAAA,IACzC;AAEA,IAAA,IAAI;AACA,MAAA,OAAO,MAAM,EAAA,CAAG,IAAA,CAAK,EAAE,CAAA;AAAA,IAC3B,SAAS,CAAA,EAAG;AACR,MAAA,IAAI,CAAC,QAAQ,OAAA,EAAS;AAClB,QAAA,OAAA,CAAQ,OAAA,GAAU,IAAA;AAAA,MACtB;AAEA,MAAA,MAAM,CAAA;AAAA,IACV,CAAA,SAAE;AACE,MAAA,IAAI,EAAE,OAAA,CAAQ,KAAA,KAAU,CAAA,EAAG;AACvB,QAAA,IAAI,QAAQ,OAAA,EAAS;AACjB,UAAA,MAAM,IAAA,CAAK,EAAA,CAAG,GAAA,CAAI,UAAU,CAAA;AAAA,QAChC,CAAA,MAAO;AACH,UAAA,MAAM,IAAA,CAAK,EAAA,CAAG,GAAA,CAAI,QAAQ,CAAA;AAAA,QAC9B;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACJ","file":"index.js","sourcesContent":["import type { Database } from \"sqlite\";\n\nimport { UnitOfWork } from \"@hexaijs/core\";\n\nexport class SqliteUnitOfWork implements UnitOfWork<Database> {\n private static transactions = new WeakMap<\n Database,\n {\n level: number;\n aborted: boolean;\n }\n >();\n\n constructor(private db: Database) {\n if (!SqliteUnitOfWork.transactions.has(db)) {\n SqliteUnitOfWork.transactions.set(db, {\n level: 0,\n aborted: false,\n });\n }\n }\n\n getClient(): Database {\n const current = SqliteUnitOfWork.transactions.get(this.db);\n if (!current || current.level === 0) {\n throw new Error(\"No transaction is active\");\n }\n return this.db;\n }\n\n async scope<T>(fn: () => Promise<T>): Promise<T> {\n return this.wrap(fn);\n }\n\n async wrap<T>(fn: (client: Database) => Promise<T>): Promise<T> {\n const current = SqliteUnitOfWork.transactions.get(this.db)!;\n if (++current.level === 1) {\n await this.db.run(\"BEGIN TRANSACTION\");\n }\n\n try {\n return await fn(this.db);\n } catch (e) {\n if (!current.aborted) {\n current.aborted = true;\n }\n\n throw e;\n } finally {\n if (--current.level === 0) {\n if (current.aborted) {\n await this.db.run(\"ROLLBACK\");\n } else {\n await this.db.run(\"COMMIT\");\n }\n }\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/sqlite-unit-of-work.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Database } from \"sqlite\";\n\nimport { TransactionHooks, UnitOfWork } from \"@hexaijs/core\";\nimport type { TransactionHook } from \"@hexaijs/core\";\n\nexport class SqliteUnitOfWork implements UnitOfWork<Database> {\n private static transactions = new WeakMap<\n Database,\n {\n level: number;\n aborted: boolean;\n hooks: TransactionHooks;\n }\n >();\n\n constructor(private db: Database) {\n if (!SqliteUnitOfWork.transactions.has(db)) {\n SqliteUnitOfWork.transactions.set(db, {\n level: 0,\n aborted: false,\n hooks: new TransactionHooks(),\n });\n }\n }\n\n getClient(): Database {\n const current = SqliteUnitOfWork.transactions.get(this.db);\n if (!current || current.level === 0) {\n throw new Error(\"No transaction is active\");\n }\n return this.db;\n }\n\n beforeCommit(hook: TransactionHook): void {\n const current = this.getRequiredState(\"beforeCommit\");\n current.hooks.addBeforeCommit(hook);\n }\n\n afterCommit(hook: TransactionHook): void {\n const current = this.getRequiredState(\"afterCommit\");\n current.hooks.addAfterCommit(hook);\n }\n\n afterRollback(hook: TransactionHook): void {\n const current = this.getRequiredState(\"afterRollback\");\n current.hooks.addAfterRollback(hook);\n }\n\n async scope<T>(fn: () => Promise<T>): Promise<T> {\n return this.wrap(fn);\n }\n\n async wrap<T>(fn: (client: Database) => Promise<T>): Promise<T> {\n const current = SqliteUnitOfWork.transactions.get(this.db)!;\n if (++current.level === 1) {\n await this.db.run(\"BEGIN TRANSACTION\");\n }\n\n let abortError: unknown;\n try {\n return await fn(this.db);\n } catch (e) {\n if (!current.aborted) {\n current.aborted = true;\n }\n abortError = e;\n\n throw e;\n } finally {\n if (--current.level === 0) {\n const hooks = current.hooks;\n const wasAborted = current.aborted;\n\n current.hooks = new TransactionHooks();\n current.aborted = false;\n\n if (wasAborted) {\n await hooks.executeRollback(\n async () => { await this.db.run(\"ROLLBACK\"); },\n abortError\n );\n } else {\n await hooks.executeCommit(\n async () => { await this.db.run(\"COMMIT\"); },\n async () => { await this.db.run(\"ROLLBACK\"); }\n );\n }\n }\n }\n }\n\n private getRequiredState(hookName: string) {\n const current = SqliteUnitOfWork.transactions.get(this.db);\n if (!current || current.level === 0) {\n throw new Error(\n `Cannot register ${hookName} hook outside of a transaction scope`\n );\n }\n return current;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,63 +1,63 @@
1
1
  {
2
- "name": "@hexaijs/sqlite",
3
- "publishConfig": {
4
- "access": "public"
2
+ "name": "@hexaijs/sqlite",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.4.0",
7
+ "type": "module",
8
+ "description": "SQLite support for hexai",
9
+ "license": "MIT",
10
+ "author": "Sangwoo Hyun <wkdny.hyun@gmail.com>",
11
+ "contributors": [
12
+ "Seungcheol Baek <victoryiron.baek@gmail.com>",
13
+ "Donghyun Lee <edonghyun@naver.com>"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/workingdanny911/hexai.git",
18
+ "directory": "packages/sqlite"
19
+ },
20
+ "homepage": "https://github.com/workingdanny911/hexai#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/workingdanny911/hexai/issues"
23
+ },
24
+ "keywords": [
25
+ "hexai",
26
+ "hexagonal",
27
+ "clean-architecture",
28
+ "sqlite",
29
+ "database",
30
+ "typescript"
31
+ ],
32
+ "files": [
33
+ "dist",
34
+ "LICENSE"
35
+ ],
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/index.js"
5
40
  },
6
- "version": "0.3.0",
7
- "type": "module",
8
- "description": "SQLite support for hexai",
9
- "license": "MIT",
10
- "author": "Sangwoo Hyun <wkdny.hyun@gmail.com>",
11
- "contributors": [
12
- "Seungcheol Baek <victoryiron.baek@gmail.com>",
13
- "Donghyun Lee <edonghyun@naver.com>"
14
- ],
15
- "repository": {
16
- "type": "git",
17
- "url": "git+https://github.com/workingdanny911/hexai.git",
18
- "directory": "packages/sqlite"
41
+ "./test": {
42
+ "types": "./dist/test/index.d.ts",
43
+ "import": "./dist/test/index.js"
19
44
  },
20
- "homepage": "https://github.com/workingdanny911/hexai#readme",
21
- "bugs": {
22
- "url": "https://github.com/workingdanny911/hexai/issues"
23
- },
24
- "keywords": [
25
- "hexai",
26
- "hexagonal",
27
- "clean-architecture",
28
- "sqlite",
29
- "database",
30
- "typescript"
31
- ],
32
- "files": [
33
- "dist",
34
- "LICENSE"
35
- ],
36
- "exports": {
37
- ".": {
38
- "types": "./dist/index.d.ts",
39
- "import": "./dist/index.js"
40
- },
41
- "./test": {
42
- "types": "./dist/test/index.d.ts",
43
- "import": "./dist/test/index.js"
44
- },
45
- "./package.json": "./package.json"
46
- },
47
- "scripts": {
48
- "test": "vitest run",
49
- "build": "tsup"
50
- },
51
- "dependencies": {},
52
- "peerDependencies": {
53
- "@hexaijs/core": "^0.7.0",
54
- "sqlite": "^5.1.1",
55
- "sqlite3": "^5.1.7"
56
- },
57
- "devDependencies": {
58
- "@hexaijs/core": "workspace:^",
59
- "@hexaijs/tooling": "workspace:*",
60
- "sqlite": "^5.1.1",
61
- "sqlite3": "^5.1.7"
62
- }
63
- }
45
+ "./package.json": "./package.json"
46
+ },
47
+ "dependencies": {},
48
+ "peerDependencies": {
49
+ "@hexaijs/core": "^0.8.0",
50
+ "sqlite": "^5.1.1",
51
+ "sqlite3": "^5.1.7"
52
+ },
53
+ "devDependencies": {
54
+ "sqlite": "^5.1.1",
55
+ "sqlite3": "^5.1.7",
56
+ "@hexaijs/core": "^0.8.0",
57
+ "@hexaijs/tooling": "0.1.0"
58
+ },
59
+ "scripts": {
60
+ "test": "vitest run",
61
+ "build": "tsup"
62
+ }
63
+ }