@bubltec/mycota-postgres 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/dist/database.d.ts +14 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +53 -0
- package/dist/database.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/migrate.d.ts +13 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +38 -0
- package/dist/migrate.js.map +1 -0
- package/dist/ports.d.ts +43 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +2 -0
- package/dist/ports.js.map +1 -0
- package/dist/postgres.spec.d.ts +2 -0
- package/dist/postgres.spec.d.ts.map +1 -0
- package/dist/postgres.spec.js +82 -0
- package/dist/postgres.spec.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PoolLike, QueryResult, SqlDatabase } from './ports.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps an injected pool. Local Docker and RDS are the same adapter —
|
|
4
|
+
* `FakeSqlDatabase` is the wrong primitive; run Postgres.
|
|
5
|
+
*/
|
|
6
|
+
export declare class PostgresDatabase implements SqlDatabase {
|
|
7
|
+
private readonly pool;
|
|
8
|
+
constructor(pool: PoolLike);
|
|
9
|
+
query<T = Record<string, unknown>>(text: string, values?: unknown[]): Promise<QueryResult<T>>;
|
|
10
|
+
inTransaction(): boolean;
|
|
11
|
+
transaction<T>(work: () => Promise<T>): Promise<T>;
|
|
12
|
+
close(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAkB,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAWrF;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,WAAW;IACtC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,QAAQ;IAErC,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAMnG,aAAa,IAAI,OAAO;IAIlB,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAoBlD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
package/dist/database.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
const tx = new AsyncLocalStorage();
|
|
3
|
+
function wrap(result) {
|
|
4
|
+
return {
|
|
5
|
+
rows: result.rows,
|
|
6
|
+
rowCount: result.rowCount ?? result.rows.length,
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Wraps an injected pool. Local Docker and RDS are the same adapter —
|
|
11
|
+
* `FakeSqlDatabase` is the wrong primitive; run Postgres.
|
|
12
|
+
*/
|
|
13
|
+
export class PostgresDatabase {
|
|
14
|
+
pool;
|
|
15
|
+
constructor(pool) {
|
|
16
|
+
this.pool = pool;
|
|
17
|
+
}
|
|
18
|
+
async query(text, values) {
|
|
19
|
+
const client = tx.getStore();
|
|
20
|
+
const result = client ? await client.query(text, values) : await this.pool.query(text, values);
|
|
21
|
+
return wrap(result);
|
|
22
|
+
}
|
|
23
|
+
inTransaction() {
|
|
24
|
+
return tx.getStore() !== undefined;
|
|
25
|
+
}
|
|
26
|
+
async transaction(work) {
|
|
27
|
+
if (tx.getStore())
|
|
28
|
+
return work();
|
|
29
|
+
const client = await this.pool.connect();
|
|
30
|
+
try {
|
|
31
|
+
await client.query('BEGIN');
|
|
32
|
+
const result = await tx.run(client, work);
|
|
33
|
+
await client.query('COMMIT');
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
try {
|
|
38
|
+
await client.query('ROLLBACK');
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// The original error is the one to surface.
|
|
42
|
+
}
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
client.release();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async close() {
|
|
50
|
+
await this.pool.end();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,MAAM,EAAE,GAAG,IAAI,iBAAiB,EAAkB,CAAC;AAEnD,SAAS,IAAI,CAAI,MAAqD;IACpE,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAW;QACxB,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM;KAChD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IACE;IAA7B,YAA6B,IAAc;QAAd,SAAI,GAAJ,IAAI,CAAU;IAAG,CAAC;IAE/C,KAAK,CAAC,KAAK,CAA8B,IAAY,EAAE,MAAkB;QACvE,MAAM,MAAM,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC/F,OAAO,IAAI,CAAI,MAAM,CAAC,CAAC;IACzB,CAAC;IAED,aAAa;QACX,OAAO,EAAE,CAAC,QAAQ,EAAE,KAAK,SAAS,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,WAAW,CAAI,IAAsB;QACzC,IAAI,EAAE,CAAC,QAAQ,EAAE;YAAE,OAAO,IAAI,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACzC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC7B,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YACjC,CAAC;YAAC,MAAM,CAAC;gBACP,4CAA4C;YAC9C,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACxB,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,SAAS,EACT,cAAc,EACd,QAAQ,EACR,WAAW,EACX,SAAS,EACT,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Migration, SqlDatabase } from './ports.js';
|
|
2
|
+
/**
|
|
3
|
+
* First migration most apps want. Product tables stay in the consuming app.
|
|
4
|
+
* RDS Postgres 16+ and the `pgvector/pgvector` image both ship the extension.
|
|
5
|
+
*/
|
|
6
|
+
export declare const VECTOR_EXTENSION: Migration;
|
|
7
|
+
/**
|
|
8
|
+
* Applies numbered SQL in order. Each migration runs inside a transaction
|
|
9
|
+
* (unless the SQL cannot — `CREATE INDEX CONCURRENTLY` belongs in its own
|
|
10
|
+
* migration and should not be mixed with this helper).
|
|
11
|
+
*/
|
|
12
|
+
export declare function applyMigrations(db: SqlDatabase, migrations: Migration[]): Promise<number>;
|
|
13
|
+
//# sourceMappingURL=migrate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AASzD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAG9B,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAe/F"}
|
package/dist/migrate.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const ENSURE_TABLE = `
|
|
2
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
3
|
+
id text PRIMARY KEY,
|
|
4
|
+
applied_at timestamptz NOT NULL DEFAULT now()
|
|
5
|
+
)
|
|
6
|
+
`;
|
|
7
|
+
/**
|
|
8
|
+
* First migration most apps want. Product tables stay in the consuming app.
|
|
9
|
+
* RDS Postgres 16+ and the `pgvector/pgvector` image both ship the extension.
|
|
10
|
+
*/
|
|
11
|
+
export const VECTOR_EXTENSION = {
|
|
12
|
+
id: 'mycota.vector',
|
|
13
|
+
sql: 'CREATE EXTENSION IF NOT EXISTS vector',
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Applies numbered SQL in order. Each migration runs inside a transaction
|
|
17
|
+
* (unless the SQL cannot — `CREATE INDEX CONCURRENTLY` belongs in its own
|
|
18
|
+
* migration and should not be mixed with this helper).
|
|
19
|
+
*/
|
|
20
|
+
export async function applyMigrations(db, migrations) {
|
|
21
|
+
await db.query(ENSURE_TABLE);
|
|
22
|
+
const applied = await db.query('SELECT id FROM schema_migrations');
|
|
23
|
+
const seen = new Set(applied.rows.map((row) => row.id));
|
|
24
|
+
let ran = 0;
|
|
25
|
+
for (const migration of migrations) {
|
|
26
|
+
if (!migration.id.trim())
|
|
27
|
+
throw new Error('migration id is required');
|
|
28
|
+
if (seen.has(migration.id))
|
|
29
|
+
continue;
|
|
30
|
+
await db.transaction(async () => {
|
|
31
|
+
await db.query(migration.sql);
|
|
32
|
+
await db.query('INSERT INTO schema_migrations (id) VALUES ($1)', [migration.id]);
|
|
33
|
+
});
|
|
34
|
+
ran += 1;
|
|
35
|
+
}
|
|
36
|
+
return ran;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAAG;;;;;CAKpB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAc;IACzC,EAAE,EAAE,eAAe;IACnB,GAAG,EAAE,uCAAuC;CAC7C,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAAe,EAAE,UAAuB;IAC5E,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,KAAK,CAAiB,kCAAkC,CAAC,CAAC;IACnF,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACxD,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QACtE,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAAE,SAAS;QACrC,MAAM,EAAE,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE;YAC9B,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,EAAE,CAAC,KAAK,CAAC,gDAAgD,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;QACnF,CAAC,CAAC,CAAC;QACH,GAAG,IAAI,CAAC,CAAC;IACX,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/ports.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One parameterized query's result. `rowCount` is 0 when the driver reports null.
|
|
3
|
+
*/
|
|
4
|
+
export interface QueryResult<T = Record<string, unknown>> {
|
|
5
|
+
rows: T[];
|
|
6
|
+
rowCount: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Narrow pool so `@bubltec/mycota-postgres` does not take a `pg` dependency.
|
|
10
|
+
* The consuming app constructs `pg.Pool` (or a test double) and passes it in.
|
|
11
|
+
*/
|
|
12
|
+
export interface PoolClientLike {
|
|
13
|
+
query(text: string, values?: unknown[]): Promise<{
|
|
14
|
+
rows: unknown[];
|
|
15
|
+
rowCount?: number | null;
|
|
16
|
+
}>;
|
|
17
|
+
release(): void;
|
|
18
|
+
}
|
|
19
|
+
export interface PoolLike {
|
|
20
|
+
query(text: string, values?: unknown[]): Promise<{
|
|
21
|
+
rows: unknown[];
|
|
22
|
+
rowCount?: number | null;
|
|
23
|
+
}>;
|
|
24
|
+
connect(): Promise<PoolClientLike>;
|
|
25
|
+
end(): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
export interface SqlClient {
|
|
28
|
+
query<T = Record<string, unknown>>(text: string, values?: unknown[]): Promise<QueryResult<T>>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Postgres (local Docker or RDS). Nested `transaction` calls join the open
|
|
32
|
+
* transaction — inner layers never import `pg`. Product tables stay in the app.
|
|
33
|
+
*/
|
|
34
|
+
export interface SqlDatabase extends SqlClient {
|
|
35
|
+
transaction<T>(work: () => Promise<T>): Promise<T>;
|
|
36
|
+
inTransaction(): boolean;
|
|
37
|
+
close(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export interface Migration {
|
|
40
|
+
id: string;
|
|
41
|
+
sql: string;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACtD,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAChG,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAChG,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IACnC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CAC/F;AAED;;;GAGG;AACH,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnD,aAAa,IAAI,OAAO,CAAC;IACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACb"}
|
package/dist/ports.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.spec.d.ts","sourceRoot":"","sources":["../src/postgres.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { PostgresDatabase } from './database.js';
|
|
3
|
+
import { applyMigrations, VECTOR_EXTENSION } from './migrate.js';
|
|
4
|
+
function recordingPool() {
|
|
5
|
+
const log = [];
|
|
6
|
+
const client = {
|
|
7
|
+
query: async (text) => {
|
|
8
|
+
log.push(text.split('\n')[0].trim());
|
|
9
|
+
if (text.includes('SELECT id FROM schema_migrations')) {
|
|
10
|
+
return { rows: [{ id: VECTOR_EXTENSION.id }], rowCount: 1 };
|
|
11
|
+
}
|
|
12
|
+
return { rows: [], rowCount: 0 };
|
|
13
|
+
},
|
|
14
|
+
release: () => {
|
|
15
|
+
log.push('RELEASE');
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
const pool = {
|
|
19
|
+
query: async (text) => {
|
|
20
|
+
log.push(`pool:${text.split('\n')[0].trim()}`);
|
|
21
|
+
if (text.includes('SELECT id FROM schema_migrations')) {
|
|
22
|
+
return { rows: [{ id: VECTOR_EXTENSION.id }], rowCount: 1 };
|
|
23
|
+
}
|
|
24
|
+
return { rows: [], rowCount: 0 };
|
|
25
|
+
},
|
|
26
|
+
connect: async () => {
|
|
27
|
+
log.push('CONNECT');
|
|
28
|
+
return client;
|
|
29
|
+
},
|
|
30
|
+
end: async () => {
|
|
31
|
+
log.push('END');
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
return { pool, log };
|
|
35
|
+
}
|
|
36
|
+
describe('PostgresDatabase', () => {
|
|
37
|
+
it('runs work on the pool outside a transaction', async () => {
|
|
38
|
+
const { pool, log } = recordingPool();
|
|
39
|
+
const db = new PostgresDatabase(pool);
|
|
40
|
+
await db.query('SELECT 1');
|
|
41
|
+
expect(log).toEqual(['pool:SELECT 1']);
|
|
42
|
+
expect(db.inTransaction()).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
it('BEGINs once and nested transaction calls join it', async () => {
|
|
45
|
+
const { pool, log } = recordingPool();
|
|
46
|
+
const db = new PostgresDatabase(pool);
|
|
47
|
+
await db.transaction(async () => {
|
|
48
|
+
await db.query('INSERT INTO t VALUES (1)');
|
|
49
|
+
await db.transaction(async () => {
|
|
50
|
+
await db.query('INSERT INTO t VALUES (2)');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
expect(log).toEqual([
|
|
54
|
+
'CONNECT',
|
|
55
|
+
'BEGIN',
|
|
56
|
+
'INSERT INTO t VALUES (1)',
|
|
57
|
+
'INSERT INTO t VALUES (2)',
|
|
58
|
+
'COMMIT',
|
|
59
|
+
'RELEASE',
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
it('rolls back and rethrows', async () => {
|
|
63
|
+
const { pool, log } = recordingPool();
|
|
64
|
+
const db = new PostgresDatabase(pool);
|
|
65
|
+
await expect(db.transaction(async () => {
|
|
66
|
+
await db.query('INSERT INTO t VALUES (1)');
|
|
67
|
+
throw new Error('boom');
|
|
68
|
+
})).rejects.toThrow('boom');
|
|
69
|
+
expect(log).toEqual(['CONNECT', 'BEGIN', 'INSERT INTO t VALUES (1)', 'ROLLBACK', 'RELEASE']);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('applyMigrations', () => {
|
|
73
|
+
it('skips ids already in schema_migrations', async () => {
|
|
74
|
+
const { pool, log } = recordingPool();
|
|
75
|
+
const db = new PostgresDatabase(pool);
|
|
76
|
+
const ran = await applyMigrations(db, [VECTOR_EXTENSION, { id: 'app.events', sql: 'CREATE TABLE events (id uuid)' }]);
|
|
77
|
+
expect(ran).toBe(1);
|
|
78
|
+
expect(log.some((line) => line.includes('CREATE TABLE events'))).toBe(true);
|
|
79
|
+
expect(log.filter((line) => line === 'CREATE EXTENSION IF NOT EXISTS vector')).toHaveLength(0);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=postgres.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.spec.js","sourceRoot":"","sources":["../src/postgres.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bubltec/mycota-postgres",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -b",
|
|
21
|
+
"typecheck": "tsc -b --noEmit",
|
|
22
|
+
"lint": "oxlint -c ../../.oxlintrc.json src",
|
|
23
|
+
"test": "vitest run"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.10.0",
|
|
27
|
+
"typescript": "^5.8.0",
|
|
28
|
+
"vitest": "^2.1.0"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/bubltec/mycota",
|
|
33
|
+
"directory": "packages/postgres"
|
|
34
|
+
}
|
|
35
|
+
}
|