@hexaijs/postgres 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -274,26 +274,92 @@ await tableManager.dropAllTables();
274
274
  await ensureConnection(client); // Safe to call multiple times
275
275
  ```
276
276
 
277
- ### Test Fixtures
277
+ ### PostgresUnitOfWorkForTesting
278
278
 
279
- The package exports test utilities from `@hexaijs/postgres/test`:
279
+ A test-specific `UnitOfWork` implementation that runs inside an external transaction. This allows tests to rollback all changes after each test, keeping the database clean without truncating tables.
280
280
 
281
281
  ```typescript
282
- import { useDatabase, useClient, useTableManager } from "@hexaijs/postgres/test";
282
+ import { PostgresUnitOfWorkForTesting } from "@hexaijs/postgres/test";
283
+ import { Client } from "pg";
284
+
285
+ describe("OrderService", () => {
286
+ let client: Client;
287
+ let uow: PostgresUnitOfWorkForTesting;
288
+
289
+ beforeEach(async () => {
290
+ client = new Client({ connectionString: "postgres://..." });
291
+ await client.connect();
292
+ await client.query("BEGIN"); // Start external transaction
293
+ uow = new PostgresUnitOfWorkForTesting(client);
294
+ });
283
295
 
284
- describe("OrderRepository", () => {
285
- // Creates database before tests, drops after
286
- const dbUrl = useDatabase("order_test_db");
296
+ afterEach(async () => {
297
+ await client.query("ROLLBACK"); // Rollback all changes
298
+ await client.end();
299
+ });
287
300
 
288
- // Provides connected client
289
- const client = useClient("order_test_db");
301
+ it("should create order", async () => {
302
+ await uow.wrap(async (c) => {
303
+ await c.query("INSERT INTO orders (id) VALUES ($1)", ["order-1"]);
304
+ });
290
305
 
291
- it("should persist orders", async () => {
292
- // Use client for assertions
306
+ // Verify within the same transaction
293
307
  const result = await client.query("SELECT * FROM orders");
294
308
  expect(result.rows).toHaveLength(1);
295
309
  });
310
+
311
+ // After this test, ROLLBACK cleans up - no data persists
312
+ });
313
+ ```
314
+
315
+ **How it works:**
316
+
317
+ | Operation | Production (`PostgresUnitOfWork`) | Testing (`PostgresUnitOfWorkForTesting`) |
318
+ |-----------|-----------------------------------|------------------------------------------|
319
+ | Start | `BEGIN` | `SAVEPOINT` |
320
+ | Commit | `COMMIT` | `RELEASE SAVEPOINT` |
321
+ | Rollback | `ROLLBACK` | `ROLLBACK TO SAVEPOINT` |
322
+
323
+ **Key behaviors:**
324
+
325
+ - **abortError propagation**: When a nested `EXISTING` operation throws (even if caught), the entire transaction is marked as aborted and will rollback - matching production behavior.
326
+ - **NESTED savepoints**: `Propagation.NESTED` creates independent savepoints that can rollback without affecting the parent.
327
+ - **Propagation.NEW**: Logs a warning and creates a new savepoint instead (true separate transactions are not possible within the external transaction).
328
+ - **Single client**: Does not support concurrent `Promise.all` wrap calls (PostgreSQL limitation with single connection).
329
+
330
+ ```typescript
331
+ // abortError behavior - matches production
332
+ await uow.wrap(async (c) => {
333
+ await insertOrder(c, 1);
334
+
335
+ try {
336
+ await uow.wrap(async () => {
337
+ throw new Error("fails");
338
+ });
339
+ } catch {
340
+ // Caught, but transaction is already marked as aborted
341
+ }
342
+
343
+ await insertOrder(c, 2); // Executes, but will be rolled back
344
+ });
345
+ // Result: Both orders rolled back (abortError propagation)
346
+
347
+ // NESTED savepoint - independent rollback
348
+ await uow.wrap(async (c) => {
349
+ await insertOrder(c, 1);
350
+
351
+ try {
352
+ await uow.wrap(async (sp) => {
353
+ await insertOrder(sp, 2);
354
+ throw new Error("fails");
355
+ }, { propagation: Propagation.NESTED });
356
+ } catch {
357
+ // Only savepoint rolled back
358
+ }
359
+
360
+ await insertOrder(c, 3);
296
361
  });
362
+ // Result: Orders 1 and 3 committed, order 2 rolled back
297
363
  ```
298
364
 
299
365
  ## API Highlights
@@ -301,6 +367,7 @@ describe("OrderRepository", () => {
301
367
  | Export | Description |
302
368
  |--------|-------------|
303
369
  | `PostgresUnitOfWork` | Transaction management with `AsyncLocalStorage` context |
370
+ | `PostgresUnitOfWorkForTesting` | Test-specific UnitOfWork that runs inside external transaction |
304
371
  | `PostgresEventStore` | Event store implementation with batch insert support |
305
372
  | `PostgresConfig` | Immutable configuration with builder pattern |
306
373
  | `postgresConfig` | Config spec for `defineConfig` integration |
@@ -1,4 +1,4 @@
1
- import type { ConfigSpec } from "@hexaijs/utils/config";
1
+ import type { ConfigSpec } from "ezcfg";
2
2
  import { PostgresConfig, type FromEnvOptions } from "./postgres-config";
3
3
  export declare class PostgresConfigSpec implements ConfigSpec<PostgresConfig> {
4
4
  private readonly prefix;
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-config-spec.d.ts","sourceRoot":"","sources":["../../src/config/postgres-config-spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExE,qBAAa,kBAAmB,YAAW,UAAU,CAAC,cAAc,CAAC;IAI7D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJzB,QAAQ,CAAC,KAAK,cAAc;gBAGP,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,cAAc,CAAC,MAAM,CAAS;IAGzD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,cAAc,GAAG,SAAS;CAQxD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAC1B,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,cAAc,CAAC,MAAM,CAAS,GACrC,kBAAkB,CAEpB"}
1
+ {"version":3,"file":"postgres-config-spec.d.ts","sourceRoot":"","sources":["../../src/config/postgres-config-spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExE,qBAAa,kBAAmB,YAAW,UAAU,CAAC,cAAc,CAAC;IAI7D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJzB,QAAQ,CAAC,KAAK,cAAc;gBAGP,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,cAAc,CAAC,MAAM,CAAS;IAGzD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,cAAc,GAAG,SAAS;CAQxD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAC1B,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,cAAc,CAAC,MAAM,CAAS,GACrC,kBAAkB,CAEpB"}
@@ -1,4 +1,4 @@
1
- import type { DatabaseConfig } from "@hexaijs/utils/config";
1
+ import type { DatabaseConfig } from "ezcfg";
2
2
  export interface PoolOptions {
3
3
  size?: number;
4
4
  connectionTimeout?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-config.d.ts","sourceRoot":"","sources":["../../src/config/postgres-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,WAAW,WAAW;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC3B;;;;OAIG;IACH,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC3B;AAED,qBAAa,cAAe,YAAW,cAAc;IACjD,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClC,SAAgB,IAAI,CAAC,EAAE,WAAW,CAAC;gBAEvB,MAAM,EAAE;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,WAAW,CAAC;KACtB;WASa,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc;IAIpD;;;;;;;;;;;;;;;OAeG;WACW,OAAO,CACjB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,cAAc,GACzB,cAAc;IAiCjB,OAAO,CAAC,MAAM,CAAC,QAAQ;IAqBhB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc;IAW9C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;IAWtC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc;IAW9C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;IAWtC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;IAWtC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;IAW1C,qBAAqB,CAAC,iBAAiB,EAAE,MAAM,GAAG,cAAc;IAWhE,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc;IAWpD,QAAQ,IAAI,MAAM;CA2B5B"}
1
+ {"version":3,"file":"postgres-config.d.ts","sourceRoot":"","sources":["../../src/config/postgres-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,MAAM,WAAW,WAAW;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC3B;;;;OAIG;IACH,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC3B;AAED,qBAAa,cAAe,YAAW,cAAc;IACjD,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClC,SAAgB,IAAI,CAAC,EAAE,WAAW,CAAC;gBAEvB,MAAM,EAAE;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,WAAW,CAAC;KACtB;WASa,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc;IAIpD;;;;;;;;;;;;;;;OAeG;WACW,OAAO,CACjB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,cAAc,GACzB,cAAc;IAiCjB,OAAO,CAAC,MAAM,CAAC,QAAQ;IAqBhB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc;IAW9C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;IAWtC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc;IAW9C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;IAWtC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;IAWtC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;IAW1C,qBAAqB,CAAC,iBAAiB,EAAE,MAAM,GAAG,cAAc;IAWhE,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc;IAWpD,QAAQ,IAAI,MAAM;CA2B5B"}
@@ -1,4 +1,4 @@
1
- export declare const getTestConfig: () => import("@hexaijs/utils/config").InferConfigType<{
1
+ export declare const getTestConfig: () => import("ezcfg").InferConfigType<{
2
2
  db: import("../config").PostgresConfigSpec;
3
3
  migrationsDir: string;
4
4
  }>;
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getTestConfig = void 0;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
8
  const config_1 = require("../config");
9
- const config_2 = require("@hexaijs/utils/config");
10
- exports.getTestConfig = (0, config_2.defineConfig)({
9
+ const ezcfg_1 = require("ezcfg");
10
+ exports.getTestConfig = (0, ezcfg_1.defineConfig)({
11
11
  db: (0, config_1.postgresConfig)("HEXAI_DB"),
12
12
  migrationsDir: node_path_1.default.join(__dirname + "/../migrations"),
13
13
  });
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/test-fixtures/config.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA6B;AAE7B,qCAA0C;AAC1C,kDAAqD;AAExC,QAAA,aAAa,GAAG,IAAA,qBAAY,EAAC;IACtC,EAAE,EAAE,IAAA,uBAAc,EAAC,UAAU,CAAC;IAC9B,aAAa,EAAE,mBAAI,CAAC,IAAI,CAAC,SAAS,GAAG,gBAAgB,CAAC;CACzD,CAAC,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/test-fixtures/config.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA6B;AAE7B,qCAA0C;AAC1C,iCAAqC;AAExB,QAAA,aAAa,GAAG,IAAA,oBAAY,EAAC;IACtC,EAAE,EAAE,IAAA,uBAAc,EAAC,UAAU,CAAC;IAC9B,aAAa,EAAE,mBAAI,CAAC,IAAI,CAAC,SAAS,GAAG,gBAAgB,CAAC;CACzD,CAAC,CAAC"}
package/dist/test.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Client } from "pg";
2
+ import { UnitOfWork } from "@hexaijs/core";
2
3
  import { TableManager } from "./helpers";
3
4
  import { PostgresConfig } from "./config";
5
+ import { PostgresTransactionOptions } from "./types";
4
6
  export declare function createTestContext(dbUrl: string | PostgresConfig): {
5
7
  client: Client;
6
8
  newClient: () => Client;
@@ -8,4 +10,16 @@ export declare function createTestContext(dbUrl: string | PostgresConfig): {
8
10
  setup: () => Promise<void>;
9
11
  teardown: () => Promise<void>;
10
12
  };
13
+ export declare class PostgresUnitOfWorkForTesting implements UnitOfWork<Client, PostgresTransactionOptions> {
14
+ private client;
15
+ private executorStorage;
16
+ constructor(client: Client);
17
+ getClient(): Client;
18
+ wrap<T = unknown>(fn: (client: Client) => Promise<T>, options?: Partial<PostgresTransactionOptions>): Promise<T>;
19
+ private getCurrentExecutor;
20
+ private resolveOptions;
21
+ private resolveExecutor;
22
+ private createExecutor;
23
+ private executeInContext;
24
+ }
11
25
  //# sourceMappingURL=test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAE5B,OAAO,EAAoC,YAAY,EAAE,MAAM,WAAW,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc;;;;iBAUpC,OAAO,CAAC,IAAI,CAAC;oBAeV,OAAO,CAAC,IAAI,CAAC;EAa3C"}
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAE5B,OAAO,EAAe,UAAU,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAoC,YAAY,EAAE,MAAM,WAAW,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,OAAO,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AAErD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc;;;;iBAUpC,OAAO,CAAC,IAAI,CAAC;oBAeV,OAAO,CAAC,IAAI,CAAC;EAa3C;AAED,qBAAa,4BACT,YAAW,UAAU,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAI7C,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,eAAe,CAAoD;gBAEvD,MAAM,EAAE,MAAM;IAE3B,SAAS,IAAI,MAAM;IAQpB,IAAI,CAAC,CAAC,GAAG,OAAO,EAClB,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAClC,OAAO,GAAE,OAAO,CAAC,0BAA0B,CAAM,GAClD,OAAO,CAAC,CAAC,CAAC;IASb,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,gBAAgB;CAM3B"}
package/dist/test.js CHANGED
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostgresUnitOfWorkForTesting = void 0;
3
4
  exports.createTestContext = createTestContext;
5
+ const node_async_hooks_1 = require("node:async_hooks");
4
6
  const pg_1 = require("pg");
7
+ const core_1 = require("@hexaijs/core");
5
8
  const helpers_1 = require("./helpers");
6
9
  const config_1 = require("./config");
7
10
  const run_hexai_migrations_1 = require("./run-hexai-migrations");
@@ -38,4 +41,216 @@ function createTestContext(dbUrl) {
38
41
  teardown,
39
42
  };
40
43
  }
44
+ class PostgresUnitOfWorkForTesting {
45
+ client;
46
+ executorStorage = new node_async_hooks_1.AsyncLocalStorage();
47
+ constructor(client) {
48
+ this.client = client;
49
+ }
50
+ getClient() {
51
+ const executor = this.getCurrentExecutor();
52
+ if (!executor) {
53
+ throw new Error("Unit of work not started");
54
+ }
55
+ return this.client;
56
+ }
57
+ async wrap(fn, options = {}) {
58
+ const resolvedOptions = this.resolveOptions(options);
59
+ const executor = this.resolveExecutor(resolvedOptions);
60
+ return this.executeInContext(executor, (exec) => exec.execute(fn, resolvedOptions));
61
+ }
62
+ getCurrentExecutor() {
63
+ return this.executorStorage.getStore() ?? null;
64
+ }
65
+ resolveOptions(options) {
66
+ return {
67
+ propagation: core_1.Propagation.EXISTING,
68
+ ...options,
69
+ };
70
+ }
71
+ resolveExecutor(options) {
72
+ if (options.propagation === core_1.Propagation.NEW) {
73
+ console.warn("[PostgresUnitOfWorkForTesting] Propagation.NEW is not fully supported in testing mode. Using savepoint instead.");
74
+ return this.createExecutor();
75
+ }
76
+ return this.getCurrentExecutor() ?? this.createExecutor();
77
+ }
78
+ createExecutor() {
79
+ return new TestTransactionExecutor(this.client);
80
+ }
81
+ executeInContext(executor, callback) {
82
+ return this.executorStorage.run(executor, () => callback(executor));
83
+ }
84
+ }
85
+ exports.PostgresUnitOfWorkForTesting = PostgresUnitOfWorkForTesting;
86
+ class TestTransactionExecutor {
87
+ client;
88
+ initialized = false;
89
+ closed = false;
90
+ abortError;
91
+ nestingDepth = 0;
92
+ savepointCounter = 0;
93
+ savepoints = [];
94
+ savepointName;
95
+ constructor(client) {
96
+ this.client = client;
97
+ this.savepointName = `test_sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
98
+ }
99
+ async execute(fn, options) {
100
+ await this.ensureStarted();
101
+ const executor = this.resolveExecutor(options.propagation);
102
+ return executor === this
103
+ ? this.runWithLifecycle(fn)
104
+ : executor.execute(fn, options);
105
+ }
106
+ getClient() {
107
+ return this.client;
108
+ }
109
+ async ensureStarted() {
110
+ if (this.initialized) {
111
+ return;
112
+ }
113
+ this.initialized = true;
114
+ await this.client.query(`SAVEPOINT ${this.savepointName}`);
115
+ }
116
+ async runWithLifecycle(fn) {
117
+ try {
118
+ return await this.executeWithNesting(fn);
119
+ }
120
+ catch (e) {
121
+ this.markAsAborted(e);
122
+ throw e;
123
+ }
124
+ finally {
125
+ await this.finalizeIfRoot();
126
+ }
127
+ }
128
+ async executeWithNesting(fn) {
129
+ this.nestingDepth++;
130
+ try {
131
+ return await fn(this.client);
132
+ }
133
+ finally {
134
+ this.nestingDepth--;
135
+ }
136
+ }
137
+ markAsAborted(error) {
138
+ this.abortError = error;
139
+ }
140
+ async finalizeIfRoot() {
141
+ if (this.nestingDepth === 0) {
142
+ await (this.isAborted() ? this.rollback() : this.commit());
143
+ }
144
+ }
145
+ resolveExecutor(propagation) {
146
+ if (this.nestingDepth === 0) {
147
+ return this;
148
+ }
149
+ return propagation === core_1.Propagation.NESTED
150
+ ? this.createSavepoint()
151
+ : (this.findActiveSavepoint() ?? this);
152
+ }
153
+ createSavepoint() {
154
+ this.savepointCounter++;
155
+ const savepoint = new TestSavepoint(`${this.savepointName}_nested_${this.savepointCounter}`, this.client, () => this.removeSavepoint());
156
+ this.savepoints.push(savepoint);
157
+ return savepoint;
158
+ }
159
+ findActiveSavepoint() {
160
+ for (let i = this.savepoints.length - 1; i >= 0; i--) {
161
+ if (!this.savepoints[i].isClosed()) {
162
+ return this.savepoints[i];
163
+ }
164
+ }
165
+ }
166
+ removeSavepoint() {
167
+ this.savepoints.pop();
168
+ }
169
+ async commit() {
170
+ if (this.closed) {
171
+ return;
172
+ }
173
+ this.closed = true;
174
+ await this.client.query(`RELEASE SAVEPOINT ${this.savepointName}`);
175
+ }
176
+ async rollback() {
177
+ if (this.closed) {
178
+ return;
179
+ }
180
+ this.closed = true;
181
+ await this.client.query(`ROLLBACK TO SAVEPOINT ${this.savepointName}`);
182
+ }
183
+ isAborted() {
184
+ return this.abortError !== undefined && !this.closed;
185
+ }
186
+ }
187
+ class TestSavepoint {
188
+ name;
189
+ client;
190
+ onClose;
191
+ initialized = false;
192
+ closed = false;
193
+ abortError;
194
+ nestingDepth = 0;
195
+ constructor(name, client, onClose) {
196
+ this.name = name;
197
+ this.client = client;
198
+ this.onClose = onClose;
199
+ }
200
+ async execute(fn) {
201
+ await this.ensureStarted();
202
+ return this.runWithLifecycle(fn);
203
+ }
204
+ isClosed() {
205
+ return this.closed;
206
+ }
207
+ async ensureStarted() {
208
+ if (this.initialized) {
209
+ return;
210
+ }
211
+ this.initialized = true;
212
+ await this.client.query(`SAVEPOINT ${this.name}`);
213
+ }
214
+ async runWithLifecycle(fn) {
215
+ this.nestingDepth++;
216
+ try {
217
+ return await fn(this.client);
218
+ }
219
+ catch (e) {
220
+ this.markAsAborted(e);
221
+ throw e;
222
+ }
223
+ finally {
224
+ this.nestingDepth--;
225
+ await this.finalizeIfRoot();
226
+ }
227
+ }
228
+ markAsAborted(error) {
229
+ this.abortError = error;
230
+ }
231
+ async finalizeIfRoot() {
232
+ if (this.nestingDepth === 0) {
233
+ await (this.isAborted() ? this.rollback() : this.commit());
234
+ }
235
+ }
236
+ async commit() {
237
+ if (this.closed) {
238
+ return;
239
+ }
240
+ this.closed = true;
241
+ await this.client.query(`RELEASE SAVEPOINT ${this.name}`);
242
+ this.onClose();
243
+ }
244
+ async rollback() {
245
+ if (this.closed) {
246
+ return;
247
+ }
248
+ this.closed = true;
249
+ await this.client.query(`ROLLBACK TO SAVEPOINT ${this.name}`);
250
+ this.onClose();
251
+ }
252
+ isAborted() {
253
+ return this.abortError !== undefined && !this.closed;
254
+ }
255
+ }
41
256
  //# sourceMappingURL=test.js.map
package/dist/test.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":";;AAMA,8CAsCC;AA5CD,2BAA4B;AAE5B,uCAA2E;AAC3E,qCAA0C;AAC1C,iEAA4D;AAE5D,SAAgB,iBAAiB,CAAC,KAA8B;IAC5D,MAAM,MAAM,GACR,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,uBAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAEtE,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC/B,MAAM,eAAe,GAAG,IAAI,yBAAe,CACvC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,CAClC,CAAC;IACF,MAAM,YAAY,GAAG,IAAI,sBAAY,CAAC,MAAM,CAAC,CAAC;IAE9C,KAAK,UAAU,KAAK;QAChB,IAAI,CAAC;YACD,MAAM,eAAe,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,IAAA,yBAAe,EAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC3C,SAAS;YACb,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,CAAC;YACZ,CAAC;QACL,CAAC;QAED,MAAM,eAAe,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,IAAA,yCAAkB,EAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,UAAU,QAAQ;QACnB,MAAM,YAAY,CAAC,KAAK,EAAE,CAAC;QAC3B,MAAM,eAAe,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,eAAe,CAAC,KAAK,EAAE,CAAC;IAClC,CAAC;IAED,OAAO;QACH,MAAM,EAAE,YAAY,CAAC,SAAS,EAAE;QAChC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,WAAM,CAAC,KAAK,CAAC;QAClC,YAAY;QACZ,KAAK;QACL,QAAQ;KACX,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.0",
6
+ "version": "0.2.1",
7
7
  "description": "PostgreSQL support for hexai",
8
8
  "license": "MIT",
9
9
  "author": "Sangwoo Hyun <wkdny.hyun@gmail.com>",
@@ -52,9 +52,9 @@
52
52
  "node-pg-migrate": "^7.9.0"
53
53
  },
54
54
  "peerDependencies": {
55
+ "ezcfg": "^0.1.0",
55
56
  "pg": "^8.0.0",
56
- "@hexaijs/core": "^0.2.0",
57
- "@hexaijs/utils": "^0.1.0"
57
+ "@hexaijs/core": "^0.2.0"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@types/pg": "^8.11.8",