@smuzi/db-postgres 0.0.3 → 0.0.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smuzi/db-postgres",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "PostgreSQL driver for @smuzi/database",
5
5
  "type": "module",
6
6
  "types": "./build/index.d.ts",
@@ -16,8 +16,8 @@
16
16
  "access": "public"
17
17
  },
18
18
  "files": [
19
- "./src",
20
- "./build"
19
+ "build/**/*.js",
20
+ "build/**/*.d.ts"
21
21
  ],
22
22
  "exports": {
23
23
  "./package.json": "./package.json",
@@ -34,14 +34,14 @@
34
34
  "#lib/*": "./src/*"
35
35
  },
36
36
  "dependencies": {
37
- "@smuzi/database": "0.0.3",
38
- "@smuzi/std": "0.2.6",
39
- "@smuzi/schema": "0.0.5",
37
+ "@smuzi/database": "0.0.5",
38
+ "@smuzi/std": "0.2.8",
39
+ "@smuzi/schema": "0.0.7",
40
40
  "pg": "^8.16.3"
41
41
  },
42
42
  "devDependencies": {
43
- "@smuzi/faker": "0.0.5",
44
- "@smuzi/tests": "0.0.3",
43
+ "@smuzi/faker": "0.0.6",
44
+ "@smuzi/tests": "0.0.5",
45
45
  "@types/node": "^22.15.21",
46
46
  "@types/pg": "^8.11.10",
47
47
  "typescript": "^7.0.2"
@@ -49,5 +49,6 @@
49
49
  "scripts": {
50
50
  "test": "tsx --env-file=.env tests/index.ts",
51
51
  "build": "tsc --project tsconfig.build.json"
52
- }
52
+ },
53
+ "main": "./build/index.js"
53
54
  }
@@ -1,11 +0,0 @@
1
- import {TDatabaseClient} from "@smuzi/database";
2
- import {Option} from "@smuzi/std";
3
-
4
- export const buildPostgresEntityRepository = (client: TDatabaseClient) => <Entity>(table: string) => {
5
- return {
6
- async find(id: number, { columns = ['*'], idColumn = 'id'}) {
7
- return (await client.query(`SELECT ${columns.join(',')} FROM ${table} where ${idColumn} = $1`, [id]))
8
- .mapOk(res => res.rows.get(0) as Option<Entity>);
9
- },
10
- }
11
- }
package/src/index.ts DELETED
@@ -1,160 +0,0 @@
1
- import { Pool } from 'pg'
2
- import {
3
- preparedSqlFromObjectToArrayParams, TableRows,
4
- TDatabaseClient, TInsertManyRowResult, TInsertRow, TInsertRowResult, DBQueryError,
5
- TQueryParams,
6
- TQueryResult
7
- } from "@smuzi/database";
8
- import {
9
- isEmpty,
10
- asArray,
11
- asObject, dump,
12
- Err,
13
- isArray, isOption,
14
- None,
15
- Ok, Option,
16
- OptionFromNullable,
17
- RecordFromKeys, Result,
18
- Simplify,
19
- Some, StdList, StdRecord
20
- } from "@smuzi/std";
21
- import {SchemaObject} from "@smuzi/schema";
22
- export * from "./migrationsLogRepository.js"
23
- export * from "./entityRepository.js"
24
-
25
- export type Config = {
26
- user: string,
27
- password: string,
28
- host: string,
29
- port: number,
30
- database: string,
31
- }
32
-
33
-
34
-
35
-
36
- export class PostgresClient implements TDatabaseClient {
37
- readonly #pool: Pool;
38
-
39
- constructor(private readonly config: Config) {
40
- this.#pool = new Pool(config)
41
-
42
- this.#pool.on('error', (err, config) => {
43
- console.error('Unexpected error on idle client', err)
44
- process.exit(-1)
45
- })
46
-
47
- }
48
-
49
- async query<S extends SchemaObject>( sql: string, params: TQueryParams = [], schema: Option<S> = None()): Promise<TQueryResult<S>> {
50
- let preparedSql = sql;
51
- let preparedParams: unknown[] = asArray(params) ? params : [];
52
-
53
- if (asObject(params)) {
54
- const preparedRes = preparedSqlFromObjectToArrayParams(preparedSql, params).unwrap();
55
- preparedSql = preparedRes.sql;
56
- preparedParams = preparedRes.params;
57
- }
58
-
59
- try {
60
- const res = await this.#pool.query({
61
- text: preparedSql,
62
- values: preparedParams,
63
- },
64
- );
65
-
66
- return Ok({
67
- rows: new TableRows(schema, res.rows),
68
- rowCount: OptionFromNullable(res.rowCount),
69
- })
70
- } catch (err) {
71
- return Err(new DBQueryError({
72
- sql: preparedSql.substring(0, 200) + (preparedSql.length > 200 ? " ..." : ""),
73
- message: err.message,
74
- code: OptionFromNullable(err.code),
75
- detail: OptionFromNullable(err.detail),
76
- table: OptionFromNullable(err.table),
77
- }));
78
- }
79
- }
80
-
81
- async insertRow<S extends SchemaObject<any>, const RC extends string[]>(
82
- table: string,
83
- schema: S,
84
- row: TInsertRow<S>,
85
- returningColumns: RC = Array<string>() as RC
86
- ): Promise<TInsertRowResult<S, RC>> {
87
- //TODO: protected for injections
88
- const columns = Object.keys(row);
89
- const values = Object.values(row).map(val => isOption(val) ? val.someOr(null) : val);
90
- const placeholders = values.map((_, index) => `$${index + 1}`).join(', ');
91
-
92
- let sql = `INSERT INTO ${table} (${columns}) VALUES (${placeholders})`;
93
- if (!isEmpty(returningColumns)) sql += ` RETURNING ${returningColumns.join(',')}` ;
94
-
95
- return (await this.query(sql, values, Some(schema))).mapOk(res => res.rows.get(0)) as TInsertRowResult<S, RC>;
96
-
97
- }
98
-
99
- async insertManyRows<S extends SchemaObject<any>, const RC extends string[]>(
100
- table: string,
101
- schema: S,
102
- rows: TInsertRow<S>[],
103
- returningColumns: RC = Array<string>() as RC
104
- ): Promise<TInsertManyRowResult<S, RC>> {
105
- if (rows.length === 0) return Ok(new TableRows(Some(schema), [])) as any;
106
-
107
- //TODO: protected for injections
108
-
109
- const columns = Object.keys(rows[0]);
110
- const values: any[] = [];
111
- const placeholders = rows.map((row, rowIndex) => {
112
- return `(${columns.map((_, colIndex) => {
113
- const placeholderIndex = rowIndex * columns.length + colIndex + 1;
114
- return `$${placeholderIndex}`;
115
- }).join(', ')})`;
116
- }).join(', ');
117
-
118
- rows.forEach(row => values.push(...Object.values(row).map(val => isOption(val) ? val.someOr(null) : val)));
119
-
120
- return (await this.query(`INSERT INTO ${table} (${columns.join(', ')}) VALUES ${placeholders} RETURNING ${returningColumns.join(',')}`, values, Some(schema))).mapOk(result => result.rows) as any;
121
- }
122
-
123
- async updateRowById<S extends SchemaObject<any>>(
124
- table: string,
125
- schema: S,
126
- id: number | string,
127
- row: Partial<TInsertRow<S>>,
128
- idColumn: string = 'id'
129
- ): Promise<TQueryResult<S>>
130
- {
131
- //TODO: protected for injections
132
- return await this.updateManyRows(table, row, `${idColumn} = ${id}`);
133
- }
134
-
135
- async updateManyRows<S extends SchemaObject<any>>(table: string, values: Partial<TInsertRow<S>>, where): Promise<TQueryResult<S>>
136
- {
137
- //TODO: protected for injections
138
- const entries = Object.entries(values);
139
-
140
- const setClause = entries
141
- .map(([key], i) => `${key} = $${i + 1}`)
142
- .join(", ");
143
-
144
- const sql = `UPDATE ${table} SET ${setClause} WHERE ${where};`;
145
-
146
- const params = entries.map(([, val]) => val);
147
-
148
- return (await this.query(sql, params));
149
- }
150
-
151
- }
152
-
153
-
154
-
155
- export function postgresClient(config: Config): TDatabaseClient {
156
- return new PostgresClient(config);
157
- }
158
-
159
-
160
-
@@ -1,69 +0,0 @@
1
- import {isEmpty, None, Option, Some} from "@smuzi/std";
2
- import {
3
- migrationLogRowSchema,
4
- TDatabaseClient,
5
- TMigrationLogAction,
6
- TMigrationLogRowSchema,
7
- TMigrationsLogRepository
8
- } from "@smuzi/database";
9
-
10
- const table = 'migrations_log';
11
-
12
- export const buildPostgresMigrationsLogRepository = (client: TDatabaseClient): TMigrationsLogRepository => {
13
- return {
14
- getTable: () => table,
15
- createTableIfNotExists() {
16
- return client.query(`CREATE TABLE IF NOT EXISTS ${table} (
17
- id SERIAL PRIMARY KEY,
18
- name VARCHAR(255) NOT NULL ,
19
- branch INTEGER NOT NULL,
20
- action VARCHAR(20),
21
- sql_source TEXT NOT NULL,
22
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
23
- );
24
-
25
- CREATE INDEX IF NOT EXISTS idx_branch_name_created ON ${table} (branch, name, created_at DESC);
26
- `)
27
- },
28
- listRuned() {
29
- return client.query<TMigrationLogRowSchema>(`SELECT * FROM ( SELECT DISTINCT ON (name) * FROM ${table} ORDER BY name, created_at DESC ) last_records WHERE action = '${TMigrationLogAction.up}'`);
30
- },
31
- listRunedByBranch(branch: number) {
32
- return client.query<TMigrationLogRowSchema>(`SELECT * FROM ( SELECT DISTINCT ON (name) * FROM ${table} WHERE branch = ${branch} ORDER BY name, created_at DESC ) last_records WHERE action = '${TMigrationLogAction.up}'`);
33
- },
34
- async getLastBranch(): Promise<Option<number>> {
35
- const res = (await client.query(`SELECT MAX(branch) as last_branch FROM ${table}`)).unwrap();
36
- if (res.rowCount.isZero()) return None();
37
-
38
- return res.rows.get(0).unwrap().get("last_branch");
39
- },
40
- create(row) {
41
- return client.insertRow(table, migrationLogRowSchema, row);
42
- },
43
- async migrationLastAction(name: string) {
44
- const res = (await client.query(`SELECT action FROM ${table} WHERE name = $1 ORDER BY created_at DESC LIMIT 1`, [name])).unwrap();
45
-
46
- if (res.rowCount.isZero()) return None();
47
-
48
- return res.rows.get(0).unwrap().get("action");
49
- },
50
- async migrationWillBeRuned(name: string)
51
- {
52
- const lastAction = await this.migrationLastAction(name);
53
-
54
- return lastAction.match({
55
- Some: (v) => v === TMigrationLogAction.up,
56
- None: () => false,
57
- })
58
- },
59
- async freshSchema() {
60
- return client.query(`DO $$ DECLARE
61
- r RECORD;
62
- BEGIN
63
- FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') LOOP
64
- EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(r.tablename) || ' CASCADE';
65
- END LOOP;
66
- END $$;`);
67
- }
68
- }
69
- }