@mikro-orm/pglite 7.2.6-dev.4 → 7.2.6-dev.40

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.
Files changed (2) hide show
  1. package/PgliteConnection.js +53 -24
  2. package/package.json +5 -5
@@ -2,6 +2,37 @@ import { PGlite } from '@electric-sql/pglite';
2
2
  import { PGliteDialect } from 'kysely';
3
3
  import array from 'postgres-array';
4
4
  import { AbstractSqlConnection, createPostgreSqlTypeParsers } from '@mikro-orm/sql';
5
+ /**
6
+ * PostgreSQL answers `COMMIT` of an aborted transaction (e.g. after a caught unique violation) with the command
7
+ * tag `ROLLBACK` instead of an error, and PGlite's `transaction()` commits without looking at the tag. Any server
8
+ * error aborts the transaction, and the only statement that then succeeds is `ROLLBACK TO SAVEPOINT`, which
9
+ * recovers it, so the last statement's outcome tells whether the commit would be a rollback.
10
+ */
11
+ const rejectAbortedCommit = (transaction) => callback => transaction(async (tx) => {
12
+ let aborted = false;
13
+ const result = await callback(Object.create(tx, {
14
+ query: {
15
+ value: async (...args) => {
16
+ try {
17
+ const res = await tx.query(...args);
18
+ aborted = false;
19
+ return res;
20
+ }
21
+ catch (e) {
22
+ // client-side failures (e.g. a serializer throwing) have no SQLSTATE and leave the transaction alone
23
+ aborted ||= !!e.code;
24
+ throw e;
25
+ }
26
+ },
27
+ },
28
+ }));
29
+ if (aborted) {
30
+ throw Object.assign(new Error('current transaction is aborted, COMMIT would be answered with ROLLBACK'), {
31
+ code: '25P02',
32
+ });
33
+ }
34
+ return result;
35
+ });
5
36
  const isInMemoryDataDir = (dataDir) => !dataDir || dataDir === 'memory://' || dataDir.startsWith('memory:');
6
37
  /**
7
38
  * Named-database mode is active when `driverOptions.dataDir` points at a real
@@ -63,30 +94,28 @@ export class PgliteConnection extends AbstractSqlConnection {
63
94
  });
64
95
  }
65
96
  }
66
- if (this.#neuterClose) {
67
- // Hand kysely a delegating wrapper rather than mutating the underlying
68
- // instance — proxies can't pierce PGlite's private fields, and patching
69
- // `close` directly would orphan a user-supplied PGlite (its real `close`
70
- // would stay neutered after `orm.close()`). Each method call hits the
71
- // original `p`, so `#private` fields stay reachable.
72
- const sharedPglitePromise = this.#pglite.then(p => ({
73
- query: p.query.bind(p),
74
- transaction: p.transaction.bind(p),
75
- close: () => Promise.resolve(),
76
- get closed() {
77
- return p.closed;
78
- },
79
- get ready() {
80
- return p.ready;
81
- },
82
- // `waitReady` is a stable Promise on the underlying instance — capture it
83
- // directly so coverage doesn't hinge on `kysely` actually awaiting it (which
84
- // only happens in the WASM-not-yet-loaded transient window).
85
- waitReady: p.waitReady,
86
- }));
87
- return new PGliteDialect({ pglite: () => sharedPglitePromise, onCreateConnection });
88
- }
89
- return new PGliteDialect({ pglite: () => this.#pglite, onCreateConnection });
97
+ // Hand kysely a delegating wrapper rather than mutating the underlying
98
+ // instance — proxies can't pierce PGlite's private fields, and patching
99
+ // `close` directly would orphan a user-supplied PGlite (its real `close`
100
+ // would stay neutered after `orm.close()`). Each method call hits the
101
+ // original `p`, so `#private` fields stay reachable.
102
+ const neuterClose = this.#neuterClose;
103
+ const pglitePromise = this.#pglite.then(p => ({
104
+ query: p.query.bind(p),
105
+ transaction: rejectAbortedCommit(p.transaction.bind(p)),
106
+ close: neuterClose ? () => Promise.resolve() : p.close.bind(p),
107
+ get closed() {
108
+ return p.closed;
109
+ },
110
+ get ready() {
111
+ return p.ready;
112
+ },
113
+ // `waitReady` is a stable Promise on the underlying instance — capture it
114
+ // directly so coverage doesn't hinge on `kysely` actually awaiting it (which
115
+ // only happens in the WASM-not-yet-loaded transient window).
116
+ waitReady: p.waitReady,
117
+ }));
118
+ return new PGliteDialect({ pglite: () => pglitePromise, onCreateConnection });
90
119
  }
91
120
  async close(force) {
92
121
  await super.close(force);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/pglite",
3
- "version": "7.2.6-dev.4",
3
+ "version": "7.2.6-dev.40",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "keywords": [
6
6
  "data-mapper",
@@ -48,9 +48,9 @@
48
48
  "copy": "node ../../scripts/copy.mjs"
49
49
  },
50
50
  "dependencies": {
51
- "@electric-sql/pglite": "0.5.8",
52
- "@mikro-orm/sql": "7.2.6-dev.4",
53
- "kysely": "0.29.6",
51
+ "@electric-sql/pglite": "0.5.13",
52
+ "@mikro-orm/sql": "7.2.6-dev.40",
53
+ "kysely": "0.29.7",
54
54
  "postgres-array": "3.0.4",
55
55
  "postgres-date": "2.1.0",
56
56
  "postgres-interval": "4.1.0"
@@ -59,7 +59,7 @@
59
59
  "@mikro-orm/core": "^7.2.5"
60
60
  },
61
61
  "peerDependencies": {
62
- "@mikro-orm/core": "7.2.6-dev.4"
62
+ "@mikro-orm/core": "7.2.6-dev.40"
63
63
  },
64
64
  "engines": {
65
65
  "node": ">= 22.17.0"